我正在写一些东西,使用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>'
答案 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());
}
答案 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
实例化中时。