在什么基础上,将由单个元素组成的参数包的表达式转换为未表示的表达式

W.F*_*.F. 9 c++ decltype language-lawyer fold-expression c++17

考虑一个例子:

#include <type_traits>

template <class... Ts>
decltype (auto) foo(Ts... ts) {
      return (ts->x + ...);
}

struct X {
    int x;
};

int main() {
    X x1{1};
    static_assert(std::is_reference_v<decltype(foo(&x1))>);
}
Run Code Online (Sandbox Code Playgroud)

[现场演示]

decltype(auto)从带括号的左值推导出的,应根据[cl.type.simple] /4.4推导出左值参考.例如:

decltype(auto) foo(X *x) { // type of result == int&
    return (x->x);
}
Run Code Online (Sandbox Code Playgroud)

但是snipped会触发static_assert.即使我们将表达式组合成额外的括号,例如:

return ((ts->x + ...));
Run Code Online (Sandbox Code Playgroud)

它不会改变效果.

标准中是否有一点可以防止将单个元素的折叠表达式推导到左值参考中?


编辑

作为Johannes Schaub的一个重点- litb clang实际上确实将代码双parens版本解释为带括号的左值并推导出左值引用.在这种情况下,我会将其解释为gcc错误.然而,具有单parens版本的版本仍然存在问题.让我感到困惑的是,至少在多个元素的情况下,必须将版本转换为带括号的代码 - 以实现运算符优先级.例如:

(x + ...)*4 -> (x1 + x2)*4
Run Code Online (Sandbox Code Playgroud)

不一致的原因是什么?

And*_*dyG 9

如果希望在单个参数的情况下返回引用,则需要一组额外的括号*,以便参数包扩展扩展引用:

template <class... Ts>
decltype (auto) foo(Ts... ts) {
      return ((ts->x) + ...);
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,在传递多个参数的场景中,结果仍然是整数的总和,它返回一个rvalue,因此静态断言将失败.


为什么不((ts->x + ...))评估参考?

因为fold表达式将返回一个到期int,然后包装int在另一个括号中仍然是一个int.当我们使用内括号时((ts->x) + ...)),返回单个参数的fold表达式int&.它适用于clang 6.0.0但不适用于gcc 8.0.0,所以我不确定.

*折叠表达式的括号(所涉及的括号(ts->x + ...))是折叠表达式的一部分; ts->x + ...单独说是不正确的.

  • 我强烈主张这个错误在GCC中.`decltype(auto)`可能不响应fold表达式的"括号",但额外的set是应该明确受到影响的东西. (4认同)
  • 顺便说一下,Clang接受并且不会失败静态断言:https://godbolt.org/g/FC8qvR (3认同)