【问题标题】:SFINAE expression fails to compile with clangSFINAE 表达式无法使用 clang 编译
【发布时间】:2016-10-01 10:11:36
【问题描述】:

这看起来像是 clang 中的一个问题(我已经打开了一个错误 here),但我想确定我没有做错。

考虑以下代码:

#include <type_traits>
#include <cstddef>

template<std::size_t N, std::size_t M, std::enable_if_t<not (N>M)>* = nullptr> // (1)
struct S: public S<N+1, M> { };

template<std::size_t N>
struct S<N, N> { };

int main() {
    S<0, 1> c{};
}

编译失败,报如下错误:

8 : 错误:非类型模板参数专门化了具有依赖类型'std::enable_if_t M)> *' (aka 'typename enable_if M), void>::type *')的模板参数
结构 S { };

使用以下行而不是 (1),相同的代码按预期工作:

template<std::size_t N, std::size_t M, typename = std::enable_if_t<not (N>M)>>

SFINAE 表达式几乎相同。它基于 std::enable_if_t 的专业化,我希望这两个示例的结果相同(成功或失败)。
我的期望错了吗?

请注意,GCC 在任何一种情况下都可以正常工作。

【问题讨论】:

  • 整件事对我来说似乎不起作用。主模板中的参数类型总是会被构造,无论是选择它还是选择特化。所以 enable_if 总是会失败,main 方法的格式会不正确。
  • 相关文本是“在引用类模板特化的类型名称中,(例如,A)参数列表应与主模板的模板参数列表匹配. 特化的模板参数是从主模板的参数推导出来的。”。 “主模板的参数”是隐含的参数列表,它在概念上遵循非特殊情况:struct S&lt;N,M,&lt;unnamed&gt;&gt;。在您的情况下,未命名参数的类型无效,这将导致代码格式错误。
  • @JohannesSchaub-litb 整个事情与 GCC 一样工作(即使它不应该,但 sfinae 表达式是有效的)。如果您在 typename = std::enable_if&lt;... 中更改它,它仍然有效,这次是使用 GCC 和 clang。例如,S&lt;0, 2&gt; 被接受,而S&lt;2, 0&gt; 不被接受。 enable_if 在第一种情况下不会失败,但在第二种情况下会失败。我不明白你的意思,对不起。
  • 我的意思是您的参数类型不是 SFINAE 上下文。您可能已经在偏特化中看到enable_if 表达式,它们位于 SFINAE 上下文中,但作为主模板中的类型,它们不是 SFINAE 上下文。并且将它作为默认参数可能会做一些你认为它做的事情:你可能期望它编译:coliru.stacked-crooked.com/a/486ba2517bf67c18
  • 我的意思是它不是一个 sfinae 表达式。这只是一个“替换失败错误”的表达。如果S&lt;N,M&gt; 中的 N>M,则您的代码总是格式错误的。那里没有 SFINAE 的“不是错误”部分。术语 SFINAE 用于替换失败但它不是错误并且可以考虑替代声明的情况(在我的示例中为S&lt;2, 1&gt;)。

标签: gcc clang c++14 sfinae enable-if


【解决方案1】:

我认为这实际上是一个 gcc 错误,是 [temp.class.spec] 的结果:

模板参数的类型对应于一个专门的 非类型参数不应依赖于 专业化。 [ 例子:

template <class T, T t> struct C {};
template <class T> struct C<T, 1>;       // error

template< int X, int (*array_ptr)[X] > class A {};
int array[5];
template< int X > class A<X,&array> { }; // error

——结束示例 ]

在您的示例中,第三个模板参数的类型取决于参数。当你把它换成typename = std::enable_if_t&lt;...&gt; 时,这条规则就不再适用了。


注意:与static_assert-ing 相比,这里有任何理由使用 SFINAE 吗?

【讨论】:

  • 有趣,我没有明白交换 (1) 会改变规则并且 sfinae 表达式不再是参数的事实。其实是有道理的。使用 sfinae 而不是静态断言的原因?嗯……不完全是,我更偏向于 sfinae 表达式,因为它们是我首先想到的,但在这里静态断言也可以,绝对。
  • 我同时向 GCC 打开了一个错误,并关闭了这个错误。感谢您对标准的引用。我会将您的答案链接为参考
  • @T.C.有解决办法吗?
猜你喜欢
  • 2011-01-08
  • 1970-01-01
  • 1970-01-01
  • 2023-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-01
相关资源
最近更新 更多