SFINAE在这里不适用吗?

问题描述 投票:3回答:3

我在写一些东西,使用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.glCQcXHr

谁能解释一下这里发生了什么,为什么?

这看起来可能与 SFINAE中使用的别名模板会导致一个硬性错误但也没有人回答这个问题。

以下是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>'                                                               
c++ c++11 templates sfinae enable-if
3个回答
9
投票

只有发生在一个 即时语境 可能导致扣款失败。

§14.8.2[临时扣除]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,这不是在一个直接的背景。


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
投票

问题是,如果类型不存在,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 实例化。

© www.soinside.com 2019 - 2024. All rights reserved.