考虑以下代码片段:
template <bool> struct B { };
template <typename T>
constexpr bool pred(T t) { return true; }
template <typename T>
auto f(T t) -> decltype(B<pred(t)>{})
{
}尽管g++的诊断具有误导性,但我认为这里的问题是t不是一个常量表达式。
decltype(B<pred(T{})>{})...fixes g++:godbolt.org上的实例上的编译错误
哪个编译器在这里运行正确?
发布于 2018-08-25 02:16:09
GCC错了,没有任何规则阻止以这种方式在常量表达式中使用函数的参数。
但是,您不能在这样的上下文中使用参数的值,而且T类型的集合( f是可调用的)受到了很大的限制。要了解为什么,我们需要考虑在计算表达式pred(t)时将计算哪些构造。
// parameters renamed for clarity
template <typename U>
constexpr bool pred(U u) { return true; }
template <typename T>
auto f(T t) -> decltype(B<pred(t)>{});调用pred(t)的计算语义如下:
pred的参数t复制初始化f的参数tpred的主体,这将创建一个bool值trueu因此,f仅对T类型可调用,而对于这些类型,上述仅涉及在常量计算期间有效的构造(规则请参见[expr.const]p2 )。所需经费如下:
T必须是文字类型u的t的复制初始化必须是一个常量表达式,特别是不能对t的任何成员执行lvalue到rvalue的转换(因为它们的值是未知的),并且不能命名t的任何引用成员。在实践中,这意味着如果f是一个空类类型,具有默认的复制构造函数,或者如果T是一个类类型,其复制构造函数是constexpr,并且不读取其参数的任何成员,则T是可调用的,或者(奇怪的是)如果T是std::nullptr_t (尽管是案例错误)。
发布于 2018-06-15 02:29:42
编译器在该上下文中需要一个参数,因为它需要计算完整(模板重载)函数类型。考虑到pred的实现,任何值都将在该位置工作。这里,它将f参数的模板类型绑定到参数。g++编译器似乎在做一个简化的假设,即模板constexpr函数将以某种方式被任何参数更改,除非它们也是const,正如您已经演示的那样,clang也同意,情况不一定是这样的。
这都取决于编译器在函数实现中有多深,因为非const对返回值的贡献而将函数标记为非const。
还有一个问题是,函数是否被实例化,并要求编译器实际编译代码,而不是执行模板解析,至少使用g++,这似乎是一个不同的编译级别。
然后我进入了标准,他们友好地允许编译器编写人员准确地认为简化的假设和模板函数实例化应该只适用于f<const T>或f <const T&>。
警员函数必须具有:它的每个参数必须是LiteralType
因此,模板代码应该编译,但如果使用非const实例化,则会失败。
发布于 2018-07-07 23:42:17
t不是常数值,这意味着pred(t)也不是常数。您不能在B<pred(t)>中使用它,因为这需要参数。
此版本正确编译:
template <bool> struct B { };
template <typename T>
constexpr bool pred(T t) { return true; }
template <typename T, T t>
auto f() -> decltype(B<pred(t)>{})
{
}另一个有效代码是:
template <typename T>
auto f(T t) -> decltype(pred(t))
{
}这是因为您不是只计算pred(t),而是获取类型信息。B<pred(t)>需要对pred(t)进行评估--从其他方面来说,您将得到B<true>或B<false>,对于任何不能这样做的正常值都是如此。
std::integral_constant<int, 0>{}可以在Clang中工作,可能是因为它的值内置为类型的一部分,并且始终是相同的。如果我们稍微修改一下代码:
template <typename T>
auto f(T t) -> decltype(B<pred(decltype(t){})>{})
{
return {};
}Clang和GCC都编译了它,以防std::integral_constant,t和decltype(t){}都有相同的值。
https://stackoverflow.com/questions/50512021
复制相似问题