Is SFINAE being used here?

I was writing something to use SFINAE so as not to generate a function under certain conditions. When I use the meta code directly, it works as expected, but when I use this code indirectly through another class, it doesn't work as expected.

I thought it was a VC ++ thing, but it looks like g ++ has this too, so I'm wondering if there is some reason SFINAE doesn't apply to this case.

The code is simple. If the class used is not the base class of the "collection class" then do not generate the function.

#include <algorithm>
#include <type_traits>

#define USE_DIRECT 0
#define ENABLE 1

class A{};
class B{};
class C{};
class D{};
class collection1 : A, B, C {};
class collection2 : D {};

#if USE_DIRECT
template<typename X>
typename std::enable_if<std::is_base_of<X, collection1>::value, X>::type fn(X x)
{
    return X();
}

# if ENABLE
template<typename X>
typename std::enable_if<std::is_base_of<X, collection2>::value, X>::type fn(X x)
{
    return X();
}
# endif

#else // USE_DIRECT

template<typename X, typename COLLECTION>
struct enable_if_is_base_of
{
    static const int value = std::is_base_of<X, COLLECTION>::value;
    typedef typename std::enable_if<value, X>::type type;
};

template<typename X>
typename enable_if_is_base_of<X, collection1>::type fn(X x)
{
    return X();
}

# if ENABLE
template<typename X>
typename enable_if_is_base_of<X, collection2>::type fn(X x)
{
    return X();
}
# endif
#endif // USE_DIRECT

int main()
{
    fn(A());
    fn(B());
    fn(C());
    fn(D());

   return 0;
}

      

If I set USE_DIRECT to 1 and ENABLE to 0, then it cannot compile as there is no function fn

that takes a parameter D

. Setting ENABLE to 1 will stop this error.

However, if I set USE_DIRECT to 0 and ENABLE to 0, it will fail with different error messages, but for the same case there is no fn

one that takes a parameter D

. However, setting ENABLE to 1 will fail for all 4 function calls.

For your confidence, here is the code in the online compiler: http://goo.gl/CQcXHr

Can someone please explain what is happening here and why?

It looks like it might have something to do with Alias ​​patterns used in SFINAE lead to hard error , but no one has answered this.

For reference, here are the errors that were generated by g ++:

main.cpp: In instantiation of 'struct enable_if_is_base_of<A, collection2>':                                                                   
main.cpp:45:53:   required by substitution of 'template<class X> typename enable_if_is_base_of<X, collection2>::type fn(X) [with X = A]'       
main.cpp:54:8:   required from here                                                                                                            
main.cpp:34:82: error: no type named 'type' in 'struct std::enable_if<false, A>'                                                               
  typedef typename std::enable_if<std::is_base_of<X, COLLECTION>::value, X>::type type;                                                        
                                                                                  ^                                                            
main.cpp: In instantiation of 'struct enable_if_is_base_of<B, collection2>':                                                                   
main.cpp:45:53:   required by substitution of 'template<class X> typename enable_if_is_base_of<X, collection2>::type fn(X) [with X = B]'       
main.cpp:55:8:   required from here                                                                                                            
main.cpp:34:82: error: no type named 'type' in 'struct std::enable_if<false, B>'                                                               
main.cpp: In instantiation of 'struct enable_if_is_base_of<C, collection2>':                                                                   
main.cpp:45:53:   required by substitution of 'template<class X> typename enable_if_is_base_of<X, collection2>::type fn(X) [with X = C]'       
main.cpp:56:8:   required from here                                                                                                            
main.cpp:34:82: error: no type named 'type' in 'struct std::enable_if<false, C>'                                                               
main.cpp: In instantiation of 'struct enable_if_is_base_of<D, collection1>':                                                                   
main.cpp:38:53:   required by substitution of 'template<class X> typename enable_if_is_base_of<X, collection1>::type fn(X) [with X = D]'       
main.cpp:57:8:   required from here                                                                                                            
main.cpp:34:82: error: no type named 'type' in 'struct std::enable_if<false, D>'                                                               

      

+3


source to share


3 answers


Only a substitution that takes place in the immediate context can lead to a deduction error:

§14.8.2 [temp.deduct] / p8

Only invalid types and expressions in the immediate context of a function type and its template parameter types can cause deduction to fail. [Note: Evaluation of overridden types and expressions can lead to side effects such as creating specialized template templates and / or specialized functions, generating implicitly defined functions, etc. Such side effects are not in the "immediate context" and can lead to poor program design. - end of note]

A fn

full specialization declaration is required to sign enable_if_is_base

:

§14.7.1 [temp.inst] / p1



Unless a class template specification has been explicitly created (14.7.2) or is explicitly specialized (14.7.3), a class template specialization is implicitly created when the specialization is referenced in a context that requires a fully qualified object type, or when the completeness of the class type affects the semantics of the program.

The compiler cannot generate a specialization:

template<typename X, typename COLLECTION>
struct enable_if_is_base_of
{
    static const int value = std::is_base_of<X, COLLECTION>::value;
    typedef typename std::enable_if<value, X>::type type;
};

      

because the substitution typename std::enable_if<value, X>::type

results in a missing type if value

evaluated as false

, which is not in the immediate context.

+8


source


As said, your SFINAE circuit cannot work. However, you can use the following not-so-pretty solution to achieve what you probably want:

#include <algorithm>
#include <type_traits>
#include <iostream>

class A{};
class B{};
class C{};
class D{};
class collection1 : A, B, C {};
class collection2 : D {};

template<typename X, class Enable = void>
struct enable_if_is_base_of;

template<typename X>
struct enable_if_is_base_of<X, typename std::enable_if<std::is_base_of<X, collection1>::value>::type> {
  static X fn(X x) {
    (void) x;
    std::cout << "collection1" << std::endl;
    return X();
  }
};

template<typename X>
struct enable_if_is_base_of<X, typename std::enable_if<std::is_base_of<X, collection2>::value>::type> {
  static X fn(X x) {
    (void) x;
    std::cout << "collection2" << std::endl;
    return X();
  }
};

int main() {
  enable_if_is_base_of<A>::fn(A());
  enable_if_is_base_of<B>::fn(B());
  enable_if_is_base_of<C>::fn(C());
  enable_if_is_base_of<D>::fn(D());
}

      



LIVE DEMO

+2


source


The problem is that if the type does not exist then the typedef is not skipped, this is an error.

Solution: no typedef

template<typename X, typename COLLECTION>
struct enable_if_is_base_of : std::enable_if<std::is_base_of<X, COLLECTION>::value, X>
{};

      

A member now type

exists (via inheritance) if and only if it exists in an instance enable_if

.

+2


source







All Articles