SFINAE不适用于此处吗?

时间:2014-12-28 17:46:33

标签: c++ c++11 sfinae

我正在写一些东西,使用SFINAE在某些条件下不生成函数。当我直接使用元代码时,它按预期工作,但是当我通过另一个类间接使用代码时,它无法按预期工作。

我认为这是一个VC ++的东西,但看起来像g ++也有这个,所以我想知道是否有一些理由说SFINAE没有适用于这种情况。

代码很简单。如果使用的类不是“集合类”的基类,则不要生成函数。

#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;
}

如果我将USE_DIRECT设置为1并将ENABLE设置为0,则无法编译,因为没有函数fn采用参数D。将ENABLE设置为1将停止发生该错误。

但是,如果我将USE_DIRECT设置为0并将ENABLE设置为0,它将失败并显示不同的错误消息,但对于相同的情况,没有fn采用参数D。但是,将ENABLE设置为1会导致所有4个函数调用失败。

为了您的方便,以下是在线编译器中的代码:http://goo.gl/CQcXHr

有人可以解释这里发生了什么以及为什么?

这看起来可能与Alias templates used in SFINAE lead to a hard error有关,但没有人回答过这个问题。

供参考,以下是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 个答案:

答案 0 :(得分:8)

只有在直接上下文中进行的替换可能会导致扣减失败:

§14.8.2[temp.deduct] / p8

  

仅在函数类型的直接上下文中使用无效的类型和表达式   其模板参数类型可能导致扣减失败。 [注意:对替代类型的评估   和表达式可能导致副作用,例如类模板特化的实例化和/或   函数模板专业化,隐式定义函数的生成等。这样的副作用是   不在“直接上下文”中,可能会导致程序格式不正确。 - 结束记录]

fn的签名需要完整声明enable_if_is_base的专业化存在:

§14.7.1[temp.inst] / p1

  

除非已明确实例化(14.7.2)或明确专门化(14.7.3)类模板特化,   在上下文中引用特化时,将隐式实例化类模板特化   这需要完全定义的对象类型,或者类类型的完整性会影响语义   该计划。

编译器无法生成:

的特化
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;
};

因为如果typename std::enable_if<value, X>::type评估为value,则false的替换会导致缺少类型,而{{1}}不是直接上下文。

答案 1 :(得分:2)

如前所述,您的SFINAE计划无法运作。但是,你可以使用以下不那么漂亮的解决方案来实现你可能想要的东西:

#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 :(得分:2)

问题是如果类型不存在,则不会跳过typedef,这是一个错误。

解决方案:没有typedef

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

现在type成员存在(通过继承)当且仅当它存在于enable_if实例化中时。