By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
448,652 Members | 1,682 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 448,652 IT Pros & Developers. It's quick & easy.

Using SFINAE with constructors

P: n/a
I'm writing a class that, depending on a template parameter, has
constructors that take differing numbers of arguments. I initially
thought that I could use SFINAE (via boost::enable_if_c) to achieve my
ends, but I have hit a snag.

If I've messed up somewhere, or there's another way to get what I'm
after, I'd be greaful if someone could help me out.

Here is a condensed version of my code, showing the problem. Note that
I get the same problem regardless of whether FIRST_ATTEMPT is defined.

//--------------------------------
template<bool B, typename T>
struct Enabler
{
};

template<typename T>
struct Enabler<true, T>
{
typedef T type;
};
#if FIRST_ATTEMPT
template<unsigned N>
class Foo
{
public:
Foo() {}
explicit Foo(typename Enabler<N == 1, int>::type x) {}
Foo(typename Enabler<N == 2, int>::type x, int y) {}
Foo(typename Enabler<N == 3, int>::type x, int y, int z) {}
};
#else
template<unsigned N>
class Foo
{
struct private_type {};
public:
Foo() {}
explicit Foo(int x, typename Enabler<N == 1, private_type>::type =
private_type()) {}
Foo(int x, int y, typename Enabler<N == 2, private_type>::type =
private_type()) {}
Foo(int x, int y, int z, typename Enabler<N == 3, private_type>::type
= private_type()) {}
};
#endif

int main()
{
Foo<0> f0;
Foo<1> f1(1);
Foo<2> f2(1,2);
Foo<3> f3(1,2,3);

return 0;
}

//--------------------------------
The compiler tells me, several times that there is no type named ‘type’
in struct Enabler; which, I thought was the point of using SFINAE (i.e.
if there is no such type, then that overload is removed from the
resolution list).

--
Clark S. Cox, III
cl*******@gmail.com

Sep 9 '05 #1
Share this Question
Share on Google+
2 Replies


P: n/a
Clark S. Cox III wrote:
I'm writing a class that, depending on a template parameter, has
constructors that take differing numbers of arguments. I initially
thought that I could use SFINAE (via boost::enable_if_c) to achieve my
ends, but I have hit a snag.

If I've messed up somewhere, or there's another way to get what I'm
after, I'd be greaful if someone could help me out.
I think you're misinterpreting how SFINAE works. The point of it, IIUIC,
is that if it fails to find (deduce) the T for a template function, that
function is not instantiated and does not participate in the overload
resolution. So, it only works when you try to instantiate the template
where it is _used_, not where it's declared.

As to another way... It may be possible if all your arguments are of
built-in types (by use of the ellipsis), but if you want a generic
solution, you will need to use macros, probably.
Here is a condensed version of my code, showing the problem. Note that I
get the same problem regardless of whether FIRST_ATTEMPT is defined.

//--------------------------------
template<bool B, typename T>
struct Enabler
{
};

template<typename T>
struct Enabler<true, T>
{
typedef T type;
};
#if FIRST_ATTEMPT
template<unsigned N>
class Foo
{
public:
Foo() {}
explicit Foo(typename Enabler<N == 1, int>::type x) {}
Foo(typename Enabler<N == 2, int>::type x, int y) {}
Foo(typename Enabler<N == 3, int>::type x, int y, int z) {}
};
#else
template<unsigned N>
class Foo
{
struct private_type {};
public:
Foo() {}
explicit Foo(int x, typename Enabler<N == 1, private_type>::type =
private_type()) {}
Foo(int x, int y, typename Enabler<N == 2, private_type>::type =
private_type()) {}
Foo(int x, int y, int z, typename Enabler<N == 3, private_type>::type =
private_type()) {}
};
#endif

int main()
{
Foo<0> f0;
Foo<1> f1(1);
Foo<2> f2(1,2);
Foo<3> f3(1,2,3);

return 0;
}

//--------------------------------
The compiler tells me, several times that there is no type named ‘type’
in struct Enabler; which, I thought was the point of using SFINAE (i.e.
if there is no such type, then that overload is removed from the
resolution list).


Yes, but functions that _might_ participate but later are discarded from
the resolution list are all *declared*. SFINAE only engages when it is
impossible to deduce template arguments _and_ come up with valid results,
not when it's impossible to *declare* a function. SFINAE can only help
skip an instantiation of a template, but not a declaration of one.

V
Sep 9 '05 #2

P: n/a
In article <2005090915453516807%clarkcox3@gmailcom>,
Clark S. Cox III <cl*******@gmail.com> wrote:
I'm writing a class that, depending on a template parameter, has
constructors that take differing numbers of arguments. I initially
thought that I could use SFINAE (via boost::enable_if_c) to achieve my
ends, but I have hit a snag.

If I've messed up somewhere, or there's another way to get what I'm
after, I'd be greaful if someone could help me out.

Here is a condensed version of my code, showing the problem. Note that
I get the same problem regardless of whether FIRST_ATTEMPT is defined.

//--------------------------------
template<bool B, typename T>
struct Enabler
{
};

template<typename T>
struct Enabler<true, T>
{
typedef T type;
};
#if FIRST_ATTEMPT
template<unsigned N>
class Foo
{
public:
Foo() {}
explicit Foo(typename Enabler<N == 1, int>::type x) {}
Foo(typename Enabler<N == 2, int>::type x, int y) {}
Foo(typename Enabler<N == 3, int>::type x, int y, int z) {}
};


Unfortunately once you instantiate a class template, you instantiate its
entire interface. SFINAE doesn't help you here. If the constructors
were member templates, then you could use SFINAE to constrain the free
template parameters on those members.

The only way I know of to get the effect you want is to specialize for
each value of N you are interested in. You could derive from an
implementation class to consolidate code that is common for all N:

template <unsigned N>
class FooImp
{
};

template<unsigned N> class Foo;

template <>
class Foo<0>
: private FooImp<0>
{
public:
Foo();
};

template <>
class Foo<1>
: private FooImp<1>
{
public:
Foo();
explicit Foo(int x);
};

template <>
class Foo<2>
: private FooImp<2>
{
public:
Foo();
Foo(int x, int y);
};

template <>
class Foo<3>
: private FooImp<3>
{
public:
Foo();
Foo(int x, int y, int z);
};

int main()
{
Foo<0> f0;
Foo<1> f1(1);
Foo<2> f2(1,2);
Foo<3> f3(1,2,3);

return 0;
}

-Howard
Sep 9 '05 #3

This discussion thread is closed

Replies have been disabled for this discussion.