为什么`std::forward_as_tuple(1)`不是常量表达式?

3
#include <tuple>

int main() {
  static_assert(std::is_same<std::tuple<int&&>,
                             decltype(std::forward_as_tuple(1))>::value, "");
  constexpr int x = 5;
  constexpr auto t1 = std::forward_as_tuple(1);  // (1)
  constexpr auto t2 = std::forward_as_tuple(x);  // (2)
  constexpr std::tuple<int&&> t3(1);             // (3)
  constexpr std::tuple<int> t4(1); // OK!
}

在上述代码中,静态断言通过了,但是第1到3行无法在gcc 4.9(由ubuntu提供)和clang中编译。它们抱怨变量没有通过constexprs初始化,x不是constexpr(即使它被文字初始化),它创建对临时值的引用或者它们的forward_as_tuple()实现不是(尽管C++14标准确保这一点)。
我正在处理一些大量使用std::tupleconstexpr的代码。我可以绕过std::forward_as_tuple()未定义为constexpr,但我不明白为什么forward_as_tuple(0)会返回一个tuple<int&&>,这根据clang的说法创建了一个对临时值的引用,从而使其不是constexpr。替代方案对我需要的内容都不起作用--std::make_tuple()不能用于完美转发,std::tie不能存储字面值。 编辑:为什么std::forward_as_tuple()以这种方式工作,并且没有提供替代方案?
我是在做一些基本错误还是有些事情我不理解?

forward_as_tuple 返回 std::tuple<Types&&...>。您期望 forward_as_tuple(0) 返回什么类型?比 int 更适合的是哪个 Types...?即,你已经演示了会发生什么:你期望每行发生什么。它用于完美转发:返回引用或右值引用元组。如果需要,可以编写存储右值的元组,而且代码并不长。 - Yakk - Adam Nevraumont
你不能有 constexpr int&& var = .. - Jarod42
如果我传递了 std::forward_as_tuple(1,x,std::move(y)),我会期望得到 std::tuple<int,X&,Y>。我不理解的是为什么 std::forward_as_tuple 认为 tuple<int&&, X& &&, Y&&> 更有用或更正确,并且没有提供一个版本,使得 forward_as_tuple 对字面量有效。换句话说,似乎 forward_as_tuple 实际上并没有正确地转发。 - SplinterOfChaos
因为能够命名类型并不意味着可以合法地创建该类型的实例。 - Billy ONeal
1个回答

0

std::forward_as_tuple 的工作方式是这样的,因为它被指定为返回引用元组以进行完美转发。如果您想要一个函数,在使用 1xstd::move(y) 调用时返回 std::tuple<int,X&,Y>,那么请编写一个:

template <typename...Ts>
constexpr std::tuple<Ts...> foo(Ts&&...ts) {
  return std::tuple<Ts...>{std::forward<Ts>(ts)...};
}

演示


不确定是我的措辞问题,但这并没有回答我的问题。所以,如果t = std::forward_as_tuple(0, x)将返回一个std::tuple<int&&, X& &&>,那么std::get<0>(t)将返回int&&std::get<1>(t)将返回X& &&,如果传递给另一个函数,它们将被完美转发。但是,通过t = foo(0, x)std::get<0>(std::move(t))std::get<1>(std::move(t)),我不会得到相同的效果吗?那也不是完美转发吗? - SplinterOfChaos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接