SFINAE,演绎与实例(SFINAE, deduction vs. instantiation)

2019-07-17 17:00发布

此代码失败,在大多数编译器来编译,但起初我直觉预期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具有一个,并且不如果它不...但没有错误无论哪种方式实例:

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

在准确的条件下可以SFINAE申请什么看14.8.2(C ++ 03),它看起来对我来说,它指定。 有没有更好的地方去寻找? 已经推导出模板实例中失败,即使在别人的演绎,似乎没有被列入这份名单。

我采取的解释是什么使这个非法的另一个方向是a_metafun扣除已经发生及其内脏的实例是什么原因造成的错误。 SFINAE实例化过程中,但只有在扣除不适用,还是我错在那里? 尽管在第二种情况下,a_metafun被正确和公formedly实例化,但是它只是内没有“类型”的定义,这意味着该模板试图初始化它未能由于置换。

基本上,我想知道在什么标准的规定,我看到的行为。 每一个编译器我试过抱怨,甚至科莫。 我认为,他们这样做的正确的,我只是不完全确定,为什么。

因此,专家们...什么是什么? 为什么类型的实例,即使在F中扣除的情况下()导致一个错误,而不是排斥SFINAE?

Answer 1:

在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引入了那句“直接背景”使文字稍微好一点,还是这句话的定义不够清晰。 这是一个积极的问题:

  • 1844年定义“直接上下文”


Answer 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的。



文章来源: SFINAE, deduction vs. instantiation
标签: c++ standards