samedi 9 octobre 2021

Return element of pair with perfect forwarding

I want to write a function that will evaluate some expression that returns a pair (possibly as a reference, and possibly containing references) and returns the second element, but which forwards references rather than copying them.

Here's an example that doesn't quite work:

#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;
}

The problem is with foo(value_of_values): value_of_values returns a prvalue, but get<1> returns an rvalue reference, so foo returns an rvalue reference to a temporary that promptly disappears.

I know I can probably do some template metaprogramming with decltype to distinguish the case where the function return value is a prvalue containing a non-reference (which must be returned as a non-reference) from the other cases, but is there a more elegant solution?

(using std::forward<F>(func)().second instead of get<1>(...) doesn't help: if it's parenthesised one gets exactly the same problem because member access on a prvalue is an xvalue, causing decltype(auto) to infer int &&, and if it's not parenthesized then the ref_of_values case returns a copy instead of a reference because of the semantics of decltype for unparenthesized member access expressions).

Aucun commentaire:

Enregistrer un commentaire