std :: apply可能无法正确实施

Nik*_*kov -1 c++ c++11 c++14 c++17

std :: apply在很少的stackoverflow答案和n3658,n3915中提到,通常定义为:

template <typename F, typename Tuple, size_t... I>
decltype(auto) apply_impl(F&& f, Tuple&& t, index_sequence<I...>) {
  return forward<F>(f)(get<I>(forward<Tuple>(t))...);
}

template <typename F, typename Tuple>
decltype(auto) apply(F&& f, Tuple&& t) {
  using Indices = make_index_sequence<tuple_size<decay_t<Tuple>>::value>;
  return apply_impl(forward<F>(f), forward<Tuple>(t), Indices{});
}
Run Code Online (Sandbox Code Playgroud)

但是,参考实现std :: apply函数无法在此类上下文中进行编译(使用clang 3.8和gcc 5.2进行测试):

std::apply ([] (int&) {} , std::make_tuple (42));
Run Code Online (Sandbox Code Playgroud)

一种可能的解决方法是简单地从apply_impl中删除std :: forward <Tuple>,但保持通用引用不变:

template <typename F, typename Tuple, size_t... I>
decltype(auto) apply_impl(F&& f, Tuple&& t, index_sequence<I...>) {
  return forward<F>(f)(get<I>(t)...);
}
Run Code Online (Sandbox Code Playgroud)

这种解决方法有任何缺点吗?有更方便的解决方案吗?

更新:另一种可能的解决方法,无需更改std :: apply(受此SO答案的启发):

template <typename T> constexpr T& make_tmp (T&& t) noexcept { return t; }
...
std::apply ([] (int& i) {} , make_tmp (std::make_tuple (42)));
Run Code Online (Sandbox Code Playgroud)

它是否正确并且结果定义明确?

Yak*_*ont 5

如果您有一个包含临时对象的临时元组(或对临时对象的引用),则需要可写引用的函数拒绝该应用程序是正确的.该函数希望调用者注意它所写的内容,并确定丢弃它.

这是编译的代码:

int main() {
  int i = 42;
  std::apply ([] (int&) {} , std::tie(i) );
}
Run Code Online (Sandbox Code Playgroud)

make_tuple创建一个副本元组. apply然后f在即将被丢弃的副本上调用传入:这些被正确地视为rvalues.

如果你有一个想要通过引用传递的对象,请std::apply在元组中引用它,而不是它的副本.然后,元组的右值岬内apply并不能适用于引用内容.

对于std::get<N>(some_tuple)返回一个右值,则:

(A)第n个元素必须是副本,元组必须是rvalue

(B)第n个元素必须是右值引用,元组必须是右值引用

通过存储左值引用,std::get永远不会返回右值引用.

您可能想要打电话std::tie或std::forward_as_tuple视情况而定. std::make_tuple当您想要在元组中创建副本时,而不是在存储对其他对象的引用时.

有一些方法可以让你通过右值作为左值,当你不希望放弃任何修改,但这些通常是不好的想法,他们应该与大警告标签客户端代码库来完成所有的地方,不含蓄.

template<class T>
T& as_lvalue( T&& t ) { return t; }
Run Code Online (Sandbox Code Playgroud)

很简单.