【问题标题】:SFINAE, deduction vs. instantiationSFINAE,演绎与实例化
【发布时间】:2012-12-31 06:10:41
【问题描述】:

这段代码在大多数编译器中都无法编译,但起初我直觉地希望 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 &lt; typename T &gt; 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 };
    

在查看 14.8.2 (C++03) 时,我觉得它准确地指定了 SFINAE 可以应用的条件。有更好的地方看吗?在已经推导出的模板的实例化中失败,即使在推导另一个模板期间,似乎也不会包含在此列表中。

我解释什么是非法的另一个方向是 a_metafun 的推断已经发生,它的内部实例化是导致错误的原因。 SFINAE 在实例化期间不适用,而只在演绎期间适用,还是我错了?但在第二种情况下,a_metafun 被正确且格式正确地实例化,但其中根本没有“类型”定义,这意味着尝试实例化它的模板由于替换而失败。

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

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

【问题讨论】:

  • 我认为这应该在 C++11 中失败,而不是在 C++03 中。 SFINAE 规则(或者更确切地说是措辞)在 C++11 中略有改变。

标签: c++ standards


【解决方案1】:

在 C++03 规范中,SFINAE 的规则有点模糊,允许编译器作者去 any 长度寻找替换失败导致 SFINAE。来自 C++03 的相关文本 §14.8.2/2 说,

- [...] 如果模板参数或函数模板的函数类型中的替换导致类型无效,则类型推导失败 [...]

它进一步解释了失败的一些原因,但没有一个真正说明在什么时候替换失败应该被视为 SFINAE。所以我猜,你的代码可能在 C++03 中运行良好(或者可能不行,这取决于编译器作者如何解释文本。无论如何这让我感到困惑)。

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

如果替换导致无效的类型或表达式,则类型推导失败。无效类型或表达式是如果使用替换参数编写的格式错误的类型或表达式。 [ 注意:访问检查是替换过程的一部分。 —end note ] 只有函数类型及其模板参数类型的直接上下文中的无效类型和表达式会导致推演失败。

“即时上下文”一词很有趣,我认为它适用于您的情况。更具体地说,元函数a_metafun 中的替换失败不被视为函数类型的“即时上下文”。它在 C++11 中格式不正确,而不是 SFINAE。

但是,尽管 C++11 引入了短语“立即上下文”以使文本稍微好一些,但该短语的定义仍然不够清晰。这是一个活跃的问题:

【讨论】:

  • 确实很有趣!我不知道 SFINAE 中的这种变化。谢谢
【解决方案2】:

SFINAE 不会在那里保护你,错误发生在 类型扣除之后。但是,这应该有效:

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

通过在默认模板参数中访问T::type,我们会在替换时发生这种情况,并在那时 SFINAE 启动。

编辑:在考虑了更多之后,我不确定您当前的实施失败的原因。我认为是因为a_metafun成员类型type,导致编译错误;如果a_metafun 根本没有type 的成员类型会有所不同。

【讨论】:

  • 查看我的答案,为什么它在 C++11 中失败。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-23
  • 2023-03-28
相关资源
最近更新 更多