2012-12-31 64 views
6

该代码失败在大多数编译器来编译,但起初我直觉预期SFINAE保护我:SFINAE,演绎与实例

typedef void (*A)(); 

template < typename T > 
struct a_metafun { typedef typename T::type type; }; 

template < typename T > 
typename a_metafun<T>::type f(T) {} 

template < typename T> 
void f(T(*)()) {} 

int main() { f(A()); } 

我可以以至少两种方式解决这个问题1)改变的“metafun” F(定义),以:

template < typename T > typename T::type f(T) {}

2)界定“a_metafun”,使得它分析T和具有类型,如果T具有一个,并且不我f,将它不...但没有实例化错误两种方式:

BOOST_MPL_HAS_XXX_TRAIT_DEF(type) 

typedef < template T, bool = has_type<T>::value > 
struct a_metafun { }; 

typedef < template T > 
struct a_metafun<T, true> { typedef typename T::type type }; 

在观看14.8.2(C++ 03),它看起来对我来说,它仅指定了下情况SFINAE可以申请什么。有更好的地方看吗?在已经推导出的模板的实例化中失败,即使在扣除另一个模板的过程中,似乎也不包括在这个列表中。

我已经采取了另一个解释是什么使得这个非法的方向是a_metafun的演绎已经发生并且其内部的实例化是导致错误的原因。 SFINAE在实例化过程中不适用,但仅在扣除过程中使用,或者我错了吗?但在第二种情况下,a_metafun正确,并且实例化良好,但它内部没有“类型”定义,这意味着尝试实例化它的模板由于替换而失败。

基本上我想知道标准中的什么指定了我目睹的行为。我试过的每一个编译器都会抱怨,甚至是冒险岛。我认为他们这样做是正确的,我只是不完全确定为什么。

那么,专家......那是什么?为什么类型的实例化,即使在f()中的演绎上下文中导致错误而不是SFINAE排除?

+0

我认为,应该在C++ 11失败,而不是在C++ 03虽然。 SFINAE规则(或者*措辞*)在C++ 11中略有改变。 – Nawaz

回答

2

SFINAE不会保护你,发生错误型扣除。然而,这应该工作:

template < typename T, typename Type = typename T::type > 
struct a_metafun { typedef Type type; }; 

通过默认的模板参数我们造成这在置换时有发生accesing T::type,而当时SFINAE

编辑:思前想后还有一些,我不确定你当前的实现失败的原因。我认为是因为a_metafun成员类型type,导致编译错误;如果a_metafun根本没有成员类型type,则会有所不同。

+0

看到我的答案为什么它在C++ 11中失败。 – Nawaz

4

在C++ 03规范中,SFINAE的规则有点含糊,允许编译器作者去的任何长度去查找替换失败导致SFINAE。相关的文字§14.8.2/ 2从C++ 03说,

- [...]如果在模板参数或函数模板结果的功能类型中的类型无效的取代,类型扣除失败[...]

它进一步解释了失败的几个原因,但他们中没有一个真正说过替代失败应该被视为SFINAE。所以我想,你的代码在C++ 03中可能工作的很好(或者根据编译器作者是如何解释文本的,这可能会让我感到困惑)。

但是,C++ 11中的措辞已经改进,消除了模糊性。它在§14.8.2/ 8中提到,

如果替换导致无效类型或表达式,则键入演绎失败。无效的类型或表达式是使用替代参数编写的格式不正确的类型或表达式。 [注意:访问检查是作为替换过程的一部分完成的。 - 注意]只有函数类型及其模板参数类型的上下文中的无效类型和表达式才会导致扣除失败。

术语“直接上下文”很有意思,我认为它适用于你的情况。更具体地说,元函数a_metafun中的替换失败不被视为函数类型的“直接上下文”。它在C++ 11中形成,而不是SFINAE。

“直接上下文”的定义在C++ 11中还不够清楚。这是一个积极的问题:

+1

的确很有意思!我没有意识到_SFINAE_中的这一变化。谢谢 –