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!
}

In the above code, that static_assert passes, however lines 1 through 3 fail to compile with both gcc 4.9 (supplied by ubuntu), and clang. They complain that the variables are not initialized by constexprs, that x is not a constexpr (even though it's initialized by a literal), that is creates a reference to a temporary or that their implementation of forward_as_tuple() is not (though the C++14 standard does guarantee this).

I'm working on some code that makes heavy use of std::tuple and constexpr. I can get around std::forward_as_tuple() not being defined as constexpr, but I can't understand why forward_as_tuple(0) would return a tuple<int&&>, which according to clang creates a reference to a temporary making it not constexpr. The alternatives don't work for what I need--std::make_tuple() can't be used for perfect forwarding and std::tie can't store literal values. Edit: Why does std::forward_as_tuple() work this way and without offering an alternative?

Am I doing something fundamentally wrong here or is there something I don't understand?

T.C.
  • 133,968
  • 17
  • 288
  • 421
SplinterOfChaos
  • 469
  • 3
  • 12
  • ... `forward_as_tuple` returns `std::tuple`. What type do you expect `forward_as_tuple(0)` to return? What `Types...` is more right than `int`? Ie, you have demonstrated what happens: what do you expect to happen (for each line). It is for perfect forwarding: it returns a tuple of references or rvalue references. One that stores rvalues can be written, and it isn't very long, if you want it. – Yakk - Adam Nevraumont Jan 05 '15 at 21:46
  • You can't have `constexpr int&& var = ..`. – Jarod42 Jan 05 '15 at 21:51
  • If I passed `std::forward_as_tuple(1,x,std::move(y))`, I would expect `std::tuple`. What I don't understand is why `std::forward_as_tuple` thinks that `tuple` is somehow more useful or correct and doesn't offer a version for which `forward_as_tuple` is valid on literals. In other words, it doesn't seem like `forward_as_tuple` is actually properly forwarding. – SplinterOfChaos Jan 05 '15 at 23:18
  • Because being able to name type does not mean it is legal to create an instance of that type. – Billy ONeal Jan 05 '15 at 23:32

1 Answers1

0

std::forward_as_tuple works that way because it's specified to return a tuple of references for perfect forwarding. If you want a function that returns std::tuple<int,X&,Y> when called with 1, x, and std::move(y), then write one:

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

DEMO

Casey
  • 41,449
  • 7
  • 95
  • 125
  • Not sure if it's how I worded it, but this does not answer my question. So, if `t = std::forward_as_tuple(0, x)` would return an `std::tuple`, then `std::get<0>(t)` would return `int&&` and `std::get<1>(t)` would return an `X& &&`, and if passed to another function, they would be perfect forwarded. But wouldn't I get the same effect by `t = foo(0, x)` and `std::get<0>(std::move(t))`, `std::get<1>(std::move(t))`? Does that not also perfect-forward? – SplinterOfChaos Jan 06 '15 at 13:55