Я тестирую универсальную ссылку со следующим кодом:
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 есть вопрос по аналогичной ошибке Сообщение об ошибке выводит конфликтующие типы для параметр 'const T' о константных ссылках.
Я также протестировал несколько других случаев, некоторые с преобразованием типов. Некоторые работают, а другие нет.
Я слышал, что универсальные ссылки, такие как T&&
, подходят ко всему. Почему здесь не получается?
Второй вопрос: как ввести attach_
, чтобы убедиться, что семантика перемещения работает как для xs
, так и для x
для соответствующего ввода? В конечном итоге я хотел бы иметь вариант следующего:
for(int i = 0; i < 100; i++)
xs = attach_(xs,values[i])
работать, не делая ненужных копий.
(Это проверено с помощью gcc4.8.1 с использованием g++ -std=c++11 test.cpp)
Спасибо
-- РЕДАКТИРОВАТЬ ---
Спасибо всем за отличные ответы.
Итак, теперь я понимаю, что в этом случае эффективно просто использовать передачу по значению и переместить T
. Предположим, что вектор xs не копируется без необходимости при передаче параметров и возврате обратно, если он используется в цикле, верно?
Я задал связанный с этим вопрос Когда ссылка на константу лучше, чем передача по значению в C++11?. Там у меня был такой пример, когда все говорили, что проход через долину — плохая идея:
int hd(vector<int> a) {
return a[0];
}
Можно ли вообще использовать универсальную ссылку для обработки как случая hd
, так и случая attach_
в этом посте, чтобы избежать ненужных копий?
Спасибо еще раз.
--- РЕДАКТИРОВАТЬ2 ---
Итак, я протестировал версии в ответах, а также справочную версию const
ниже. Оптимизация не используется для выявления потенциальных проблем. Версия const
ref является худшей, так как она принудительно копирует. Все остальное имеет ту же скорость, если для вектора используется std::move(a)
, за исключением необработанных вызовов 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;
}