C++11 Make_Pair With Specified Template Parameters Doesn't Compile

C++11 make_pair with specified template parameters doesn't compile

This is not how std::make_pair is intended to be used; you are not supposed to explicitly specify the template arguments.

The C++11 std::make_pair takes two arguments, of type T&& and U&&, where T and U are template type parameters. Effectively, it looks like this (ignoring the return type):

template <typename T, typename U>
[return type] make_pair(T&& argT, U&& argU);

When you call std::make_pair and explicitly specify the template type arguments, no argument deduction takes place. Instead, the type arguments are substituted directly into the template declaration, yielding:

[return type] make_pair(std::string&& argT, int&& argU);

Note that both of these parameter types are rvalue references. Thus, they can only bind to rvalues. This isn't a problem for the second argument that you pass, 7, because that is an rvalue expression. s, however, is an lvalue expression (it isn't a temporary and it isn't being moved). This means the function template is not a match for your arguments, which is why you get the error.

So, why does it work when you don't explicitly specify what T and U are in the template argument list? In short, rvalue reference parameters are special in templates. Due in part to a language feature called reference collapsing, an rvalue reference parameter of type A&&, where A is a template type parameter, can bind to any kind of A.

It doesn't matter whether the A is an lvalue, an rvalue, const-qualified, volatile-qualified, or unqualified, an A&& can bind to that object (again, if and only if A is itself a template parameter).

In your example, we make the call:

make_pair(s, 7)

Here, s is an lvalue of type std::string and 7 is an rvalue of type int. Since you do not specify the template arguments for the function template, template argument deduction is performed to figure out what the arguments are.

To bind s, an lvalue, to T&&, the compiler deduces T to be std::string&, yielding an argument of type std::string& &&. There are no references to references, though, so this "double reference" collapses to become std::string&. s is a match.

It's simple to bind 7 to U&&: the compiler can deduce U to be int, yielding a parameter of type int&&, which binds successfully to 7 because it is an rvalue.

There are lots of subtleties with these new language features, but if you follow one simple rule, it's pretty easy:

If a template argument can be deduced from the function arguments, let it be deduced. Don't explicitly provide the argument unless you absolutely must.

Let the compiler do the hard work, and 99.9% of the time it'll be exactly what you wanted anyway. When it isn't what you wanted, you'll usually get a compilation error which is easy to identify and fix.

std::make_pair, c++11 and explicit template parameters

Rvalue references happened. Where std::make_pair in C++03 has the signature

template< class T1, class T2 >
std::pair<T1,T2> make_pair( T1 t, T2 u );

In C++11, it has

template< class T1, class T2 >
std::pair<V1,V2> make_pair( T1&& t, T2&& u );

V1 and V2 are (usually) std::decay<T1|T2>::type. C++14 adds constexpr, but that does concern us here.

This means that the explicit function template specialization std::make_pair<int*, int**> accepted, in C++03, parameters of type int* and int**, while the new one in C++11 accepts int*&& and int**&&.

Binding 0 to int**&& is not a problem, but param is an lvalue and cannot be bound to an rvalue reference to int*. And that is why your code explodes in C++11.

For this reason,

return std::make_pair<int*&, int**>(param, 0);

works with both C++03 and C++11 -- param can be bound to int*&, and the resulting std::pair<int*&, int**> can be converted to the std::pair<int*, int**> that the function wants to return in both revisions.

That is rather ugly, though, and std::make_pair is not really meant to be used this way. As @T.C. points out in the comments, if you know the types the std::pair should have, just use

return std::pair<int*, int**>(param, 0);

Why does make_pairint,int fail in C++ 11?

std::make_pair has changed between c++03 and c++11.

Your code fails as std::make_pair expects T&& and U&&, where T and U are template type parameters.
Like

template <typename T, typename U>
std::pair<V1,V2> make_pair(T&& argT, U&& argU);

Still if you want to specify both of these parameter use pair<int, int>

Example -

#include <iostream>
#include <utility>
using namespace std;

int main() {
int u = 1;
pair<int, int> p = pair<int, int>(0, u);
cout << p.first << " " << p.second << "\n";
}

You can refer here for more details - utility-make_pair

Declaring template types to call to make_pair

std::make_pair takes a "universal reference" to its arguments. A universal reference can bind to pretty much anything. This is the actual signature:

template< class T1, class T2 >
constexpr std::pair<V1,V2> make_pair( T1&& t, T2&& u );
// ^^^^^^^^^^^^^^

When you explicitly supply the type (unnecessarily I might add), it turns the function call into this:

... ( int&& t, bool&& u );

Meaning the arguments are no longer universal references but rvalue-references. Rvalue-references can only bind to rvalues, not lvalues, hence the error for the first argument.

The solution to this issue is to allow template argument deduction to do its job:

map_.insert(std::make_pair(x, true));

Valid C++03 template code won't compile in C++11

Clang++ gives me a warning in -std=c++03 mode:

test.cpp:6:43: warning: use of right-shift operator ('>>') in template argument
will require parentheses in C++11 [-Wc++11-compat]
value = 1 + number_of_bits<number >> 1>::value
^
( )

And indeed, in C++11 the parsing rules were revised so that >> always closes template parameters in template context. As the warning notes, you should just put parens around the parameter to fix the parsing issue:

value = 1 + number_of_bits<(number >> 1)>::value

std::make_pair : cannot convert 'ch' (type 'char') to type 'char&&'

SOLUTION:

Instead of explicitly specifying the template arguments to make_pair<>() this way:

std::make_pair< char, unsigned >( ch, number )

Just let them be deduced:

std::make_pair( ch, number )

EXPLANATION:

The rationale behind this guideline is found in the way std::make_pair<>() is defined, and in the way template argument deduction works for universal references. From Paragraph 20.3.3/8-9 of the C++11 Standard:

template <class T1, class T2>
pair<V1, V2> make_pair(T1&& x, T2&& y);

Returns: pair<V1, V2>(std::forward<T1>(x), std::forward<T2>(y));
where V1 and V2 are determined as follows: Let Ui be decay<Ti>::type for each Ti. Then each Vi is X& if Ui equals reference_wrapper<X>, otherwise Vi is Ui. [ Example: In place of:

return pair<int, double>(5, 3.1415926); // explicit types

a C++ program may contain:

return make_pair(5, 3.1415926); // types are deduced

end example ]

Here, T1 and T2 are meant to be deduced. By explicitly specifying the template arguments yourself, you're getting on the way of make_pair<>()'s type deduction machinery to produce the correct return type, forcing the instantiation of a function which accepts an rvalue reference to char and and rvalue reference to unsigned:

... make_pair(char&&, unsigned&&)

However, you are not providing rvalues in input, because ch and number are lvalues. This is what the compiler is complaining about.


ALTERNATIVE:

Also notice, that you can implicitly construct an std::pair object, which saves your from calling make_pair<>() at all:

vec.push_back( { ch, number } );

Frustrated when creating a pair of integers in C++

Change to:

std::pair<int,int> mypair = std::make_pair(x,y);

Explanation here

Variadic template wrapper around thread constructor fails to compile

You are getting this error because std::thread decay-copies the arguments given to its constructor before calling the initial function. You are passing in a string literal with 16 characters plus a terminating null---that is, an lvalue of type const char[17]. The decay-copy of this expression produces a prvalue of type const char* (the usual array-to-pointer conversion). However, you are telling std::thread to use your run function still expecting a const char (&)[17] argument. This array-to-pointer decay is irreversible so you cannot get the required lvalue back from std::thread's decayed copy.

Here is an example of how a snippet of your code might be written to compile:

template <class Function, class... Args>
void
spawn (Function&& function, Args&&... args)
{
std::thread t (&State::run <std::decay_t<Function>, std::decay_t<Args>...>, this, std::forward<Function>(function), std::forward<Args>(args)...);
t . join ();
}

template <class Function, class... Args>
void
run (Function function, Args... args)
{
std::cout << this -> message << std::endl;
function(args...);
}

Here, we are instantiating State::run with the decayed types, ensuring that we are passing a function with the appropriate signature to receive the decay-copied arguments that will be given by std::thread.



Related Topics



Leave a reply



Submit