推导参数"T"的冲突类型以供通用参考 [英] deduced conflicting types for parameter 'T' for universal reference

查看:154
本文介绍了推导参数"T"的冲突类型以供通用参考的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用以下代码测试通用引用,

I am testing universal reference 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
}

出现错误:

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对类似错误有疑问错误消息";推断出与常量引用有关的参数常量T"" 的冲突类型.

SO has a question for a similar error Error message "deduced conflicting types for parameter 'const T'" about const references.

我还测试了其他一些情况,其中一些进行了类型转换.有些工作而另一些则没有.

I also tested a few other cases, some with type conversions. Some work and others don't.

我听说像T&&这样的通用引用可以匹配所有内容.为什么它在这里失败?

I heard that universal references like T&& matches everything. Why does it fail here?

第二个问题是,如何键入attach_以确保移动语义对于适当的输入对于xsx均适用?最终,我希望具有以下变体:

A second question is, how to type attach_ to make sure that move semantics work both for xs and x for the appropriate input? Ultimately, I'd like to have variant of the following:

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

无需复制不必要的副本即可.

work without making unnecessary copies.

(使用gcc -std = c ++ 11 test.cpp在gcc4.8.1上进行了测试)

(This is tested with gcc4.8.1, using g++ -std=c++11 test.cpp)

谢谢

-编辑---

感谢大家的精彩回答.

因此,我现在了解到,在这种情况下,仅使用按值传递并移动T是有效的.可以认为向量xs不必在参数传递和返回时被不必要地复制,如果在循环中使用,对吗?

So I understand now that for this case, it's efficient just to use pass-by-value, and move the T. It take it that the vector xs is not copied unnecessarily in parameter-passing and returning back, if used in a loop, right?

我问了一个相关问题何时是const引用比C ++ 11中的按值传递更好吗?.在那里,我有一个例子,每个人都说过路是个坏主意:

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

在本文中是否可以使用通用引用同时处理hdattach_情况,以避免不必要的复制?

Is it possible at all to use universal reference to handle both the hd case and the attach_ case in this post to avoid unnecessary copies?

再次感谢.

-编辑2 ---

因此,我测试了答案中的版本以及下面的const参考版本.优化不用于暴露任何潜在的问题. const参考版本是最差的,因为它会强制复制.如果将std::move(a)用作引导程序,则其他所有内容的速度都相同,但原始push_call调用速度更快.我想优化可以消除这种差异.我猜测试(或者可能是int类型)不够大,无法显示push_back(x)push_back(std::move(x))

So, I tested the versions in the answers plus a const reference version below. Optimization is not used to expose any potential issues. The const ref version is the worst as it forces copying. Everything else has the same speed if std::move(a) is used for the vector, except raw push_call calls are faster. I guess optimization can remove that difference. I guess 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;

}

推荐答案

通用引用的工作方式是这样的:如果您传入一个右值,则T将被推导出为int(或其他一些非-引用类型),因为T&&是右值引用类型.但是,如果您传递左值,则T将被推导为int&(或其他一些左值引用类型),因为T&&将是左值引用类型(因为左值引用和右值引用合拢" "合并为左值引用.

The way universal references work is like this: if you pass in an rvalue, then T will be deduced as int (or some other non-reference type), because then T&& is an rvalue reference type. But if you pass in an lvalue, then T will be deduced as int& (or some other lvalue reference type), because then T&& will be an lvalue reference type (since an lvalue reference and an rvalue reference "collapse" together into an lvalue reference).

因此,在传递左值的情况下,您会遇到问题,因为当T是引用类型时,您将没有vector<T>.

So in the case that you pass in an lvalue, you have a problem because you can't have vector<T> when T is a reference type.

您应该按值传递

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

这看起来效率较低,但事实并非如此.如果您传入一个右值,它将被一次移入x,并再次被移入向量.如果传入左值,则将其一次复制到x中,然后将它们移到向量中.就像通过引用传递一样:左值一个副本,右值零个副本.

This might look less efficient, but it isn't. If you pass in an rvalue, it'll be moved once into x, and moved once again into the vector. If you pass in an lvalue, it'll be copied once into x, and them moved into the vector. It's the same as if you passed by reference: one copy for an lvalue, zero copies for an rvalue.

出于教育目的,您可以使用通用参考书来做到这一点:

For educational purposes, you could do this with your universal reference:

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 you pass an lvalue, the vector element type is the non-reference type. But it really is better to just pass by value.

这篇关于推导参数"T"的冲突类型以供通用参考的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆