4

我想编写一个函数,该函数将评估一些返回一对(可能作为引用,可能包含引用)并返回第二个元素的表达式,但它转发引用而不是复制它们。

这是一个不太有效的示例:

#include <utility>
#include <type_traits>
#include <iostream>

using namespace std;

template<typename F>
decltype(auto) foo(F&& func)
{
    return get<1>(std::forward<F>(func)());
}

pair<int, int> value_of_values()
{
    return make_pair(2, 3);
}

pair<int &, int &> value_of_refs()
{
    static int x = 4;
    static int y = 5;
    return pair<int &, int &>(x, y);
}

pair<int, int> &ref_of_values()
{
    static pair<int, int> p(6, 7);
    return p;
}

int main()
{
    cout << foo(value_of_values) << '\n';
    cout << foo(value_of_refs) << '\n';
    cout << foo(ref_of_values) << '\n';
    return 0;
}

问题在于foo(value_of_values):value_of_values返回一个纯右值,但get<1>返回一个右值引用,因此 foo 返回一个对立即消失的临时的右值引用。

我知道我可能可以做一些模板元编程decltype来区分函数返回值是包含非引用(必须作为非引用返回)的纯右值的情况与其他情况,但是有没有更优雅的解决方案?

(使用std::forward<F>(func)().second而不是get<1>(...)没有帮助:如果用括号括起来,则会出现完全相同的问题,因为对prvalue的成员访问是一个xvalue,导致decltype(auto)推断int &&,如果没有括号,则ref_of_values案例返回副本而不是引用,因为无decltype括号成员访问表达式的语义)。

4

1 回答 1

4

我知道我可能可以使用 decltype 进行一些模板元编程,以区分函数返回值是包含非引用(必须作为非引用返回)的纯右值的情况与其他情况,但是有没有更优雅解决方案?

可以先用通用引用接收 的返回值func,然后根据是否为右值来决定是否移动构造该对象。

template<typename F>
decltype(auto) foo(F&& func)
{
  auto&& x = std::get<1>(std::forward<F>(func)());
  using T = decltype(x);
  if constexpr (std::is_lvalue_reference_v<T>)
    return x;
  else
    return std::remove_reference_t<T>(std::move(x));
}
于 2021-10-09T16:31:41.290 回答