【问题标题】:Is substitution failure an error with dependent non-type template parameters?替换失败是依赖非类型模板参数的错误吗?
【发布时间】:2012-04-16 19:41:48
【问题描述】:

假设我有这些模板别名:

enum class enabler {};

template <typename T>
using EnableIf = typename std::enable_if<T::value, enabler>::type;
template <typename T>
using DisableIf = typename std::enable_if<!T::value, enabler>::type;

我可以在 GCC 中执行以下操作:

#include <iostream>

template <typename T, EnableIf<std::is_polymorphic<T>> = {}>
void f(T) { std::cout << "is polymorphic\n"; }

template <typename T, DisableIf<std::is_polymorphic<T>> = {}>
void f(T) { std::cout << "is not polymorphic\n"; }

struct foo { virtual void g() {} };

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

打印出来:

是多态的
不是多态的

这符合我的期望。

使用 clang 代码无法编译。它会产生以下错误消息。

test.cpp:11:58: error: expected expression
template <typename T, EnableIf<std::is_polymorphic<T>> = {}>
                                                         ^
test.cpp:14:59: error: expected expression
template <typename T, DisableIf<std::is_polymorphic<T>> = {}>
                                                          ^
test.cpp:20:3: error: no matching function for call to 'f'
  f(foo {});
  ^
test.cpp:12:6: note: candidate template ignored: couldn't infer template argument ''
void f(T) { std::cout << "is polymorphic\n"; }
     ^
test.cpp:15:6: note: candidate template ignored: couldn't infer template argument ''
void f(T) { std::cout << "is not polymorphic\n"; }
     ^
test.cpp:21:3: error: no matching function for call to 'f'
  f(int {});
  ^
test.cpp:12:6: note: candidate template ignored: couldn't infer template argument ''
void f(T) { std::cout << "is polymorphic\n"; }
     ^
test.cpp:15:6: note: candidate template ignored: couldn't infer template argument ''
void f(T) { std::cout << "is not polymorphic\n"; }
     ^
4 errors generated.

它应该编译吗?这两个编译器哪个有问题?

【问题讨论】:

  • 哎呀,我觉得很傻。我觉得这与模板别名无关,因此标题可能具有误导性:S 抱歉,如果情况属实,我会调查一下并修正标题。
  • DisableIf&lt;std::is_polymorphic&lt;T&gt;&gt; = {} 是合法的初始化列表初始化吗?结构可以是模板值参数吗?
  • @jpalecek 不,结构不能。这就是我使用枚举的原因:)
  • 如果我不使用别名而只是手动“内联”它们,Clang 会发出类似的错误消息,因此我修复了标题。
  • @jpalecek 这是枚举的name。它指的是enum {} 有效,但enum class {} 无效。在这里完全不相关。

标签: c++ templates c++11 sfinae template-aliases


【解决方案1】:

首先,感谢#llvm IRC Channel on oftc 上的@Richard Smith 的解释。
不幸的是,这不是合法的 C++,因此 Clang 是正确的:{} 不是表达式,而是 braced-init-list,因此永远不会是 constant非类型模板参数的初始化程序中需要的表达式。

§14.3.2 [temp.arg.non-type] p1

非类型、非模板的模板参数应为以下之一:

  • 对于整数或枚举类型的非类型模板参数模板参数类型的转换常量表达式(5.19);或
  • [...]

一种解决方案是enabler 中的虚拟值。

【讨论】:

  • 这不是一个好的参数——我们不是在讨论模板参数(没有人尝试过,例如f&lt;int,{}&gt;),而是默认参数,它具有参数声明的语法,它可以,原则上,在 rhs 上有{}(如果enabler{} 是一个常量表达式,或者x,给定enabler x{},常量应该没有问题)。但是,8.3.6/3 说在模板参数声明的情况下应该有一个表达式。
  • @jpalecek: "一个人尝试例如f&lt;int,{}&gt;" 呃,调用f&lt;int&gt; 确实完全这样做。
  • @GManNickG:不,它没有。默认参数未指定(至少明确)作为文本替换。
  • @jpalecek:默认参数与正式参数有何不同?在任何情况下,您都不能只使用{},因为它永远不可能是一个常量表达式,而这是初始化非类型参数所需要的。
  • @jpalecek 是 14.1 模板参数的第 9 段“A default template-argument 是在 = 之后指定的 template-argument (14.3)在 模板参数 中。”在这种情况下缺少的论点?
猜你喜欢
  • 2015-10-11
  • 2014-06-07
  • 2018-05-01
  • 2019-07-31
  • 2022-01-15
  • 1970-01-01
  • 1970-01-01
  • 2013-06-20
相关资源
最近更新 更多