gpt4 book ai didi

c++ - 推断出参数 'T' 的冲突类型以供通用引用

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:11:06 25 4
gpt4 key购买 nike

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

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 有一个类似错误的问题 Error message "deduced conflicting types for parameter 'const T'"关于 const 引用。

我还测试了其他一些案例,其中一些案例进行了类型转换。有些有效,有些则无效。

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

第二个问题是,如何键入 attach_ 以确保移动语义对 xsx 都有效以进行适当的输入?最后,我想要以下变体:

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

工作时不要制作不必要的拷贝。

(这是用gcc4.8.1测试的,使用g++ -std=c++11 test.cpp)

谢谢

-- 编辑 ---

感谢大家的精彩回答。

所以我现在明白了,对于这种情况,使用按值传递并移动 T 是很有效的。如果在循环中使用, vector xs 不会在参数传递和返回过程中被不必要地复制,对吗?

我问了一个相关问题When is a const reference better than pass-by-value in C++11? .在那里,我有这个例子,每个人都说 pass-by-vale 是个坏主意:

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

是否可以使用通用引用来处理这篇文章中的 hd 情况和 attach_ 情况以避免不必要的复制?

再次感谢。

--- 编辑2 ---

因此,我测试了答案中的版本以及下面的 const 引用版本。优化不用于暴露任何潜在问题。 const ref 版本是最差的,因为它强制复制。如果将 std::move(a) 用于 vector ,则其他一切都具有相同的速度,除了原始 push_call 调用更快。我想优化可以消除这种差异。我想测试(或者可能是 int 类型)不够大,无法显示 push_back(x)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&&将是左值引用类型(因为左值引用和右值引用“折叠”在一起成为左值引用)。

所以在你传递一个左值的情况下,你有一个问题,因为你不能有 vector<T>什么时候T是引用类型。

你应该只按值传递,

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

这可能看起来效率较低,但事实并非如此。如果你传入一个右值,它将被移动一次到 x 中, 并再次移动到 vector 中。如果你传入一个左值,它将被复制一次到 x 中, 他们移动到 vector 中。这与通过引用传递相同:左值一个拷贝,右值零个拷贝。

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

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

这确保当您传递一个左值时, vector 元素类型是非引用类型。但按值传递确实更好。

关于c++ - 推断出参数 'T' 的冲突类型以供通用引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24621954/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com