From: Andy Venikov on
To do type introspection most of the time we use compile errors about non-existing types combined with SFINAE.

But I thought that any compile error during an instantiation of a class template would disable that overload from the list of viable instantiations suppressing the actual error.

Turns out not to be the case.

For example, try implementing has_foo<> by taking an address of foo. If foo exists, everything fine. If foo doesn't exist, then you'll get compile-time error no matter what.

Code:

template <bool Test, typename T = void>
struct enable_if;

template <typename T>
struct enable_if<true, T>
{
typedef T type;
static const bool value = true;
};

template <typename T, typename Enable = void>
struct has_foo
{
static const bool value = false;
};

template <typename T>
struct take_address_of_foo
{
static const size_t nTest = sizeof(&T::foo);
static const bool value = enable_if<nTest>::value;
};

template <typename T>
struct has_foo<T, typename enable_if<take_address_of_foo<T>::value>::type>
{
static const bool value = true;
};


struct WithFoo
{
int foo(int, int, int);
};

struct NoFoo
{
};


bool b1 = has_foo<WithFoo>::value; //compiles fine, produces true
bool b2 = has_foo<NoFoo>::value; //compile-time error
bool b3 = has_foo<int>::value; //compile-time error



What kinds of error can and can't be used with SFINAE?

Thanks,
Andy.

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Johannes Schaub (litb) on
Andy Venikov wrote:

> To do type introspection most of the time we use compile errors about
> non-existing types combined with SFINAE.
>
> But I thought that any compile error during an instantiation of a class
> template would disable that overload from the list of viable
> instantiations suppressing the actual error.
>
> Turns out not to be the case.
>
> For example, try implementing has_foo<> by taking an address of foo. If
> foo exists, everything fine. If foo doesn't exist, then you'll get
> compile-time error no matter what.
>
> Code:
>
> template <bool Test, typename T = void>
> struct enable_if;
>
> template <typename T>
> struct enable_if<true, T>
> {
> typedef T type;
> static const bool value = true;
> };
>
> template <typename T, typename Enable = void>
> struct has_foo
> {
> static const bool value = false;
> };
>
> template <typename T>
> struct take_address_of_foo
> {
> static const size_t nTest = sizeof(&T::foo);
> static const bool value = enable_if<nTest>::value;
> };
>
> template <typename T>
> struct has_foo<T, typename enable_if<take_address_of_foo<T>::value>::type>
> {
> static const bool value = true;
> };
>
>
> struct WithFoo
> {
> int foo(int, int, int);
> };
>
> struct NoFoo
> {
> };
>
>
> bool b1 = has_foo<WithFoo>::value; //compiles fine, produces true
> bool b2 = has_foo<NoFoo>::value; //compile-time error
> bool b3 = has_foo<int>::value; //compile-time error
>
>
>
> What kinds of error can and can't be used with SFINAE?
>

If a class is instantiated, it's too late for SFINAE to catch errors. SFINAE works at deduction time. Things that immediately have to do with argument deduction and substitution of the deduced arguments into function parameter lists are detected. Errors
that result from the generation of functions or classes, even if the corresponding entities were created because of a deduction going on somewhere, won't be detected at all, neither will errors that result from access violations or from implicit
definitions. For example if in a SFINAE context you do "sometype()" and that causes the default constructor of "sometype" to be defined and for some reason the definition is ill-formed (for example if "sometype" has a const datamember or reference
member), it's a hard error and not a SFINAE error.

The FCD says:

"[ Note: The evaluation of the substituted types and expressions can result in side effects such as the instantiation of class template specializations and/or function template specializations, the generation of implicitly-
defined functions, etc. Such side effects are not in the “immediate context” and can result in the program being ill-formed. — end note ]"

In your case, "take_address_of_foo<NoFoo>" is instantiated, and the error, namely "sizeof(&NoFoo::foo)", has nothing to do with deduction of the template arguments of the partial specialization of has_foo. I imagine it by thinking of the instantiated
class as being self-contained in itself, and not dependent on how it was brought into being.

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Andy Venikov on
Johannes Schaub (litb) wrote:
<snip>
> If a class is instantiated, it's too late for SFINAE to catch errors. SFINAE works at deduction time. Things that immediately have to do with argument deduction and substitution of the deduced arguments into function parameter lists are detected. Errors that result from the generation of functions or classes, even if the corresponding entities were created because of a deduction going on somewhere, won't be detected at all, neither will errors that result from access violations or from implicit definitions. For example if in a SFINAE context you do "sometype()" and that causes the default constructor of "sometype" to be defined and for some reason the definition is ill-formed (for example if "sometype" has a const datamember or reference member), it's a hard error and not a SFINAE error.

Hmm, still don't get it.
According to your description, enable_if wouldn't work either. When we say eanble_if<false, type>::type we DO instantiate enable_if, and according to your description, it's too late and we should get a hard error about non-existing typedef type of
enable_if<false, type>

In my example, I'm doing the (almost) the same thing as enable_if: we try to instantiate take_address_of_foo<NoFoo>::value (as opposed to trying to instantiate enable_if<false, type>::type) and the instantiation fails do to an error. Overload is not
selected. The only thing that's different is the type of the error.


Thanks,
Andy.

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Johannes Schaub (litb) on
Andy Venikov wrote:

> Johannes Schaub (litb) wrote:
> <snip>
>> If a class is instantiated, it's too late for SFINAE to catch errors.
>> SFINAE works at deduction time. Things that immediately have to do with
>> argument deduction and substitution of the deduced arguments into
>> function parameter lists are detected. Errors that result from the
>> generation of functions or classes, even if the corresponding entities
>> were created because of a deduction going on somewhere, won't be detected
>> at all, neither will errors that result from access violations or from
>> implicit definitions. For example if in a SFINAE context you do
>> "sometype()" and that causes the default constructor of "sometype" to be
>> defined and for some reason the definition is ill-formed (for example if
>> "sometype" has a const datamember or reference member), it's a hard error
>> and not a SFINAE error.
>
> Hmm, still don't get it.
> According to your description, enable_if wouldn't work either. When we say
> eanble_if<false, type>::type we DO instantiate enable_if, and according to
> your description, it's too late and we should get a hard error about
> non-existing typedef type of enable_if<false, type>
>

No. instantiation of "enable_if<false, type>" doesn't fail. It's the access
to "::type", which happens during deduction at the very toplevel, which
fails.

> In my example, I'm doing the (almost) the same thing as enable_if: we try
> to instantiate take_address_of_foo<NoFoo>::value (as opposed to trying to
> instantiate enable_if<false, type>::type) and the instantiation fails do
> to an error. Overload is not selected. The only thing that's different is
> the type of the error.
>

Nope see above :)


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: DeMarcus on
On 2010-05-14 01:37, Andy Venikov wrote:
> Johannes Schaub (litb) wrote:
> <snip>
>> If a class is instantiated, it's too late for SFINAE to catch errors.
>> SFINAE works at deduction time. Things that immediately have to do
>> with argument deduction and substitution of the deduced arguments into
>> function parameter lists are detected. Errors that result from the
>> generation of functions or classes, even if the corresponding entities
>> were created because of a deduction going on somewhere, won't be
>> detected at all, neither will errors that result from access
>> violations or from implicit definitions. For example if in a SFINAE
>> context you do "sometype()" and that causes the default constructor of
>> "sometype" to be defined and for some reason the definition is
>> ill-formed (for example if "sometype" has a const datamember or
>> reference member), it's a hard error and not a SFINAE error.
>
> Hmm, still don't get it.
> According to your description, enable_if wouldn't work either. When we
> say eanble_if<false, type>::type we DO instantiate enable_if, and
> according to your description, it's too late and we should get a hard
> error about non-existing typedef type of
> enable_if<false, type>
>
> In my example, I'm doing the (almost) the same thing as enable_if: we
> try to instantiate take_address_of_foo<NoFoo>::value (as opposed to
> trying to instantiate enable_if<false, type>::type) and the
> instantiation fails do to an error. Overload is not
> selected. The only thing that's different is the type of the error.
>

Could you solve it using lazy_enable_if? See Section 3.3.

http://www.boost.org/doc/libs/1_43_0/libs/utility/enable_if.html






--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]