首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用函数参数作为常量表达式的一部分- gcc对clang

使用函数参数作为常量表达式的一部分- gcc对clang
EN

Stack Overflow用户
提问于 2018-05-24 14:34:46
回答 3查看 866关注 0票数 24

考虑以下代码片段:

代码语言:javascript
复制
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)>{})
{
}
  • clang++ (主干)编译代码
  • g++ (主干)编译失败,出现以下错误: src:7:34:错误:模板参数1无效自动f( t ) ->解密类型(B{})^ src:7:34:错误:模板参数1无效src:7:25:错误:无效模板-id auto ( t) ->解密类型(B{})^ src:7:36:错误:类模板参数推导失败: auto f(T ) ->解密类型(B{})^ src:7:36:错误:对‘B() src:1:24:备注:候选人:'template B()-> B< >‘模板B{ };^ src:1:24:注:模板参数演绎/替换失败: src:7:36:注意:无法推断模板参数‘auto f( t ) -> decltype(B{}) ^ godbolt.org上的实例

尽管g++的诊断具有误导性,但我认为这里的问题是t不是一个常量表达式。

代码语言:javascript
复制
decltype(B<pred(T{})>{})

...fixes g++:godbolt.org上的实例上的编译错误

哪个编译器在这里运行正确?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2018-08-25 02:16:09

GCC错了,没有任何规则阻止以这种方式在常量表达式中使用函数的参数。

但是,您不能在这样的上下文中使用参数的值,而且T类型的集合( f是可调用的)受到了很大的限制。要了解为什么,我们需要考虑在计算表达式pred(t)时将计算哪些构造。

代码语言:javascript
复制
// 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)的计算语义如下:

  1. pred的参数t复制初始化f的参数t
  2. 评估pred的主体,这将创建一个booltrue
  3. 摧毁u

因此,f仅对T类型可调用,而对于这些类型,上述仅涉及在常量计算期间有效的构造(规则请参见[expr.const]p2 )。所需经费如下:

  • T必须是文字类型
  • 来自ut的复制初始化必须是一个常量表达式,特别是不能对t的任何成员执行lvalue到rvalue的转换(因为它们的值是未知的),并且不能命名t的任何引用成员。

在实践中,这意味着如果f是一个空类类型,具有默认的复制构造函数,或者如果T是一个类类型,其复制构造函数是constexpr,并且不读取其参数的任何成员,则T是可调用的,或者(奇怪的是)如果Tstd::nullptr_t (尽管是案例错误)。

票数 4
EN

Stack Overflow用户

发布于 2018-06-15 02:29:42

编译器在该上下文中需要一个参数,因为它需要计算完整(模板重载)函数类型。考虑到pred的实现,任何值都将在该位置工作。这里,它将f参数的模板类型绑定到参数。g++编译器似乎在做一个简化的假设,即模板constexpr函数将以某种方式被任何参数更改,除非它们也是const,正如您已经演示的那样,clang也同意,情况不一定是这样的。

这都取决于编译器在函数实现中有多深,因为非const对返回值的贡献而将函数标记为非const。

还有一个问题是,函数是否被实例化,并要求编译器实际编译代码,而不是执行模板解析,至少使用g++,这似乎是一个不同的编译级别。

然后我进入了标准,他们友好地允许编译器编写人员准确地认为简化的假设和模板函数实例化应该只适用于f<const T>f <const T&>

警员函数必须具有:它的每个参数必须是LiteralType

因此,模板代码应该编译,但如果使用非const实例化,则会失败。

票数 0
EN

Stack Overflow用户

发布于 2018-07-07 23:42:17

t不是常数值,这意味着pred(t)也不是常数。您不能在B<pred(t)>中使用它,因为这需要参数。

此版本正确编译:

代码语言:javascript
复制
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)>{})
{
}

https://godbolt.org/g/ydbj1X

另一个有效代码是:

代码语言:javascript
复制
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中工作,可能是因为它的值内置为类型的一部分,并且始终是相同的。如果我们稍微修改一下代码:

代码语言:javascript
复制
template <typename T>
auto f(T t) -> decltype(B<pred(decltype(t){})>{})
{
    return {};
}

Clang和GCC都编译了它,以防std::integral_constanttdecltype(t){}都有相同的值。

票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50512021

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档