std::apply可能未正确实现

std::apply may not be properly implemented

本文关键字:实现 apply std      更新时间:2023-10-16

std::apply在少数stackerflow答案和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{});
}

然而,引用实现std::apply函数未能在这样的上下文中编译(用clang 3.8和gcc 5.2测试):

std::apply ([] (int&) {} , std::make_tuple (42));

一种可能的解决方法是简单地删除std::forward<元组>来自apply_inpl,但保留通用参考:

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)...);
}

这种变通方法有什么缺点吗?有更方便的解决方案吗?

更新:另一个不更改的可能解决方法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)));

它是否正确,结果是否明确?

当您有一个包含临时对象(或对临时对象的引用)的临时元组时,要求可写引用的函数可以拒绝应用程序。该函数希望调用方注意它所写的内容,而你却坚决放弃了它

以下是编译的代码:

int main() {
  int i = 42;
  std::apply ([] (int&) {} , std::tie(i) );
}

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

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

对于std::get<N>(some_tuple)返回右值,可以是:

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

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

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

根据具体情况,您可能需要调用std::tiestd::forward_as_tuplestd::make_tuple用于在元组中创建副本,而不是存储对其他对象的引用。

do想要放弃任何修改时,有一些方法可以允许您将右值作为左值传递,但这些通常是坏主意,应该在到处都是大警告标签的客户端代码中执行,而不是在库中隐式执行。

template<class T>
T& as_lvalue( T&& t ) { return t; }

是一个简单的问题。