mdjdrn1
mdjdrn1

Reputation: 51

Returning std::make_pair with std::forward

I'm trying to create a function that will return pair containing two data types, where first type is always the same, but second is template-type. Is that even possible? (I'm also wondering if my understanding of std::forward usage is correct). For better demonstrating of my problem I'll show you simplified sample of my (not working) code.

Here's what I tried:

template <class X>
std::pair<int, X> func(X&& second)
{
    int first = 1;
    return std::make_pair(std::move(first), std::forward<X>(second));
}

Inside function, I create variable first and then I want to return pair. Here I move first - to avoid copying - and (following Scott Meyers' lecture where he explained std::forward as an "conditional move") depending if parameter second was l-value I want to pass second as an l-value (to let std::make_pair make copy of second, when creating pair) or if it was r-value I want to pass "moved" r-value.

Unfortunately my code doesn't work. I think I must have misunderstood something, but I don't know what, can you explain me, please?

Upvotes: 1

Views: 2184

Answers (1)

skypjack
skypjack

Reputation: 50540

I suspect you want a C++14-ish std::decay_t or a std::remove_reference_t in your declaration:

template <class X>
std::pair<int, std::decay_t<X>> func(X&& second) {
    int first = 1;
    return std::make_pair(std::move(first), std::forward<X>(second));
}

Or the C++11, more verbose, but equivalent version:

template <class X>
std::pair<int, typename std::decay<X>::type> func(X&& second) {
    int first = 1;
    return std::make_pair(std::move(first), std::forward<X>(second));
}

This way, no matter if you are using an lvalue reference or an rvalue reference to X as an actual parameter of func, your std::pair will use X as a type.


Note that you can't use a reference to X as a type of a std::pair.
Let's consider the following example:

#include<utility>

template <class X>
std::pair<int, X> func(X&& second) {
    int first = 1;
    return std::make_pair(std::move(first), std::forward<X>(second));
}

int main() {
    int i = 42;
    func(i);
}

In this case, X is deduced as int &. Return type is thus std::pair<int, int &>, that is not allowed and doesn't compile.

The following will compile instead:

template <class X>
std::pair<int, std::remove_reference_t<X>> func(X&& second) {
    int first = 1;
    return std::make_pair(std::move(first), std::forward<X>(second));
}

This is because X is still deduced as int &, but the return type is now adjusted to std::pair<int, int>.

Upvotes: 4

Related Questions