Inconsistent types deduced for parameter 'T' for universal reference

I am testing a universal link with the following code,

template <typename T> vector<T> attach_(vector<T> xs, T&& x) { xs.push_back(std::forward<T>(x)); return xs; } int main() { int k = 2; attach_(std::move(vector<int>{1,2,3}),k); //not OK attach_(std::move(vector<int>{1,2,3}),(int&)k); //not OK attach_(std::move(vector<int>{1,2,3}),(int)k); //OK attach_(std::move(vector<int>{1,2,3}),2); //OK } 

and received an error message:

 no matching function for call to 'attach_(std::remove_reference<std::vector<int> >::type, int&)' attach_(std::move(vector<int>{1,2,3}),k); note: template argument deduction/substitution failed: note: deduced conflicting types for parameter 'T' ('int' and 'int&') attach_(std::move(vector<int>{1,2,3}),k); 

SO has a question for a similar error. The error message "deduced inconsistent types for the 'const T' parameter" about const references.

I also checked several other cases, some with type conversions. Some work and others not.

I have heard that universal links like T&& fit all. Why is this happening here?

The second question is how to enter attach_ to make sure that the move semantics work for both xs and x for the corresponding input? Ultimately, I would like to have a variant of the following:

 for(int i = 0; i < 100; i++) xs = attach_(xs,values[i]) 

work without having to make unnecessary copies.

(This is verified with gcc4.8.1 using g ++ -std = C ++ 11 test.cpp)

thanks

- EDIT ---

Thanks everyone for the great answers.

So now I understand that for this case it’s useful to just use pass-by-value and move T He believes that the xs vector is not copied unnecessarily when passing parameters and going back if it is used in a loop, right?

I asked a related question When is a const reference better than pass-by-value in C ++ 11? . There I had this example where everyone said pass-by-vale is a bad idea:

 int hd(vector<int> a) { return a[0]; } 

Is it even possible to use a universal link to handle both the hd case and the attach_ case in this message to avoid unnecessary copies?

Thanks again.

--- EDIT2 ---

So, I tested the versions in the answers plus the const reference below. Optimization is not used to identify any potential problems. The ref const version is the worst since it forces you to copy. Everything else has the same speed if std::move(a) used for the vector, except for raw push_call calls faster. I think optimization can eliminate this difference. I think the test (or maybe the int type) is not big enough to show the difference between push_back(x) and push_back(std::move(x))

 #include <vector> #include <iostream> #include <chrono> using namespace std; template <class T> vector<T> attach(vector<T> v, T x) { v.push_back(x); return v; } template <typename T> vector<T> attach1(vector<T> xs, T x) { xs.push_back(std::move(x)); return xs; } template <typename T, typename E = typename std::remove_reference<T>::type> std::vector<E> attach2(std::vector<E> xs, T&& x) { xs.push_back(std::forward<T>(x)); return xs; } template <typename C, typename T> C attach3(C&& xs, T&& x) { xs.push_back(std::move<T>(x)); return std::forward<C>(xs); } template <class T> vector<T> attach4(const vector<T>& v, T x) { vector<T> ret = v; ret.push_back(x); return std::move(ret); } using namespace std::chrono; int main() { int N = 100000; vector<int> a; auto time = high_resolution_clock::now(); for (int i = 0; i < N; i++) { //a.push_back(i); //0s //a = attach(a,i); //15s //a = attach(std::move(a),i); //0.03s //a = attach2(std::move(a),i); //0.03s a = attach3(std::move(a),i); //0.03s //a = attach4(std::move(a),i); //14.9s } cout << duration_cast<duration<double>>(high_resolution_clock::now() - time).count() << endl; } 
+6
source share
3 answers

The work of universal links is this: if you pass an rvalue, then T will be displayed as int (or some other type without a link), because then T&& is the reference type of rvalue, But if you pass in the value of lvalue, then T will be displayed as int& ( or some other lvalue reference type) because then T&& will be the lvalue reference type (since the lvalue reference and the rvalue reference are "collapsed" together into the lvalue reference).

So in case you pass the value of lvalue, you have a problem because you cannot have vector<T> when T is a reference type.

You just have to pass the value,

 template <typename T> std::vector<T> attach_(std::vector<T> xs, T x) { xs.push_back(std::move(x)); return xs; } 

It may look less efficient, but it is not. If you pass the rvalue value, it will be moved once to x and moved to the vector again. If you pass in an lvalue, it will be copied once in x , and they will be moved to the vector. This is the same as if you passed by reference: one copy for lvalue, zero copies for rvalue.

For educational purposes, you can do this using a universal link:

 template <typename T, typename E = typename std::remove_reference<T>::type> std::vector<E> attach_(std::vector<E> xs, T&& x) { xs.push_back(std::forward<T>(x)); return xs; } 

This ensures that when passing the value of l, the type of the vector element is not a reference type. But it’s best to just pass by value.

+7
source

I have heard that universal links like T&& fit all. Why is this happening here?

The reason universal links match everything is because the template argument inference rules say that when T && paired with an l value of type X , T is output as X& , and then dropping the links makes X& && become X& .

In attach_(std::move(vector<int>{1,2,3}),k); the compiler prints T as int from the first parameter ( vector<T> ↔ vector<int> ), and from the second parameter T prints as int & , since k is the value of l. Thus, you get an error message.

The second question is how to enter attach_ to make sure that the move semantics work for both xs and X for the corresponding input?

The easiest way would be to simply pass it by value and move it to a vector:

 template <typename T> vector<T> attach_(vector<T> xs, T x) { xs.push_back(std::move(x)); return xs; } 

X will still move around the path if it passed with std::move .


Edit: if you are working with a large inherited type just for copying, then making two copies, as in the above case, is not ideal. In this case, you can do what @Deduplicator showed in your answer:

 template <typename T> vector<typename std::remove_reference<T>::type> attach_(vector<typename std::remove_reference<T>::type> xs, T&& x) { xs.push_back(std::forward<T>(x)); return xs; } 

Edit 2:

So now I understand that for this case it is efficient to use pass-by-value and move T He believes that the xs vector is not copied unnecessarily when passing parameters and going back if it is used in a loop, right?

The general rule is: "pass by value if it is small, or if you need to make a copy in any case, and by reference otherwise." In attach_ you need to make a copy of X (by push_back paste it into the vector), therefore passing it by value and then moving it in order.

Whether a vector should be passed by value depends on your intended semantics. If attach_(xs, x) should not be changed xs , you will need to make a copy of the vector to return in any case, and therefore you must pass it by value. However, when you do xs = attach_(xs, x); You will receive a copy. xs = attach_(std::move(xs), x); will not carry a copy, but has some small additional overhead from the movement design associated with the purpose of the movement.

If attach_(xs, x) should change xs , then pass it with a non-constant link. No overhead.

Is it even possible to use a universal link to handle both the hd case and the attach_ case in this message to avoid unnecessary copies?

hd does not need a universal link. You just index the vector, so just pass it by the const link.

+4
source

The universal reference semantics works in accordance with this quote:

8.3.2 References Β§6

If typedef-name (7.1.3, 14.1) or decltype (7.1.6.2) specifies the type TR, which is a reference to type T, an attempt to create the type "lvalue reference to cv TR" creates the type "lvalue reference to T", in while trying to create a type of "rvalue reference to cv TR" creates a type of TR. [Example:

 int i; typedef int& LRI; typedef int&& RRI; LRI& r1 = i; // r1 has the type int& const LRI& r2 = i; // r2 has the type int& const LRI&& r3 = i; // r3 has the type int& RRI& r4 = i; // r4 has the type int& RRI&& r5 = 5; // r5 has the type int&& decltype(r2)& r6 = i; // r6 has the type int& decltype(r2)&& r7 = i; // r7 has the type int& 

-end example]

Reasonable use of std::remove_reference resolves your error:

 #include <vector> using namespace std; template <typename T> vector<typename std::remove_reference<T>::type> attach_(vector<typename std::remove_reference<T>::type> xs, T&& x) { xs.push_back(std::forward<T>(x)); return xs; } int main() { int k = 2; attach_(std::move(vector<int>{1,2,3}),k); //now OK attach_(std::move(vector<int>{1,2,3}),(int&)k); //now OK attach_(std::move(vector<int>{1,2,3}),(int)k); //OK attach_(std::move(vector<int>{1,2,3}),2); //OK } 

In any case, are you sure you want to transfer the container at a cost?
Also, why not give it your own template parameter to make the function more general?

 template <typename T, typename C> void attach_(C& xs, T&& x) { xs.push_back(std::forward<T>(x)); } 

Returning the container is no longer needed ...

+2
source

Source: https://habr.com/ru/post/971892/


All Articles