#c #compiler-errors #c 11 #rvalue-reference #perfect-forwarding
#c #ошибки компилятора #c 11 #rvalue-ссылка #идеальная пересылка
Вопрос:
Я пытаюсь создать класс, который имеет два вектора больших последовательностей.
std::vector<double> factory() {
return std::vector<double>{1,2,3}; // it actually generates a large sequence of double
}
struct my_class {
my_class(const std::vector<double>amp; x, const std::vector<double>amp; y)
: m_x(x), m_y(y)
{ }
std::vector<double> m_x;
std::vector<double> m_y;
};
int main() {
my_class c(factory(), factory());
my_class c2(factory(), {0.5, 1, 1.5});
}
Ну, это работает хорошо, но он не использует конструктор перемещения vector. Итак, я попытался изменить конструктор, чтобы принимать ссылки на значения r с идеальной пересылкой.
struct my_class {
template<typename X, typename Y>
my_class(Xamp;amp; x, Yamp;amp; y
, typename std::enable_if<std::is_convertible<X, std::vector<double> >::value amp;amp;
std::is_convertible<Y, std::vector<double> >::value>::type * = 0
)
: m_x(std::forward<X>(x)), m_y(std::forward<Y>(y))
{ }
std::vector<double> m_x;
std::vector<double> m_y;
};
И теперь у меня проблема. Когда я пытаюсь создать экземпляр с initializer_list , я получаю ошибку, подобную этой.
$ g -W -Wall -std=gnu 0x a.cpp
a.cpp: In function ‘int main()’:
a.cpp:34:32: error: no matching function for call to ‘my_class::my_class(std::vector<double>, <brace-enclosed initializer list>)’
a.cpp:17:18: note: candidate is: my_class::my_class(const my_classamp;)
Я думал, что это std::initializer_list<double>
может быть не конвертируемо в std::vector<double>
, но на самом деле это конвертируемо, и я получил ту же ошибку, когда попытался без аргумента enable_if. Я что-то упускаю?
Комментарии:
1. Из любопытства, какую версию g вы используете? IIRC
initializer_list
поддержка была неполной в нескольких последних версиях.2. @awoodland Я использую gcc 4.6.2. Что вы имеете в виду под ‘imcomplete’?
Ответ №1:
Предпочтительная идиома — передавать по значению, а затем вручную перемещать внутри списка инициализаторов элементов:
struct my_class {
my_class(std::vector<double> x, std::vector<double> y)
: m_x(std::move(x)), m_y(std::move(y))
{ }
std::vector<double> m_x;
std::vector<double> m_y;
};
Это будет работать со всеми возможными аргументами и будет достаточно быстрым:
- Если вы передадите вектор lvalue, вектор будет скопирован в
x
, а затем перемещен вm_x
. - Если вы передадите вектор rvalue, вектор будет перемещен в
x
, а затем снова перемещен вm_x
. - Если вы передадите список инициализаторов,
x
он будет инициализирован из этого списка, а затем перемещен вm_x
.
Альтернативой является идеальная пересылка, но это затрудняет клиенту понимание того, что он может передать:
struct my_class {
template<typename T, typename U>
my_class(Tamp;amp; x, Uamp;amp; y)
: m_x(std::forward<T>(x)), m_y(std::forward<U>(y))
{ }
std::vector<double> m_x;
std::vector<double> m_y;
};
Кроме того, я получаю кучу предупреждений в g , поэтому я бы не рекомендовал это. Просто упомяну это для полноты картины.
Комментарии:
1. Передача по значению сгенерирует копию аргументов, чего именно хочет избежать пользователь1030861, если я правильно понимаю вопрос.
2. @Gabriel: Это зависит от категории значений аргумента. Если аргумент является значением rvalue, вызов по значению будет перемещать аргумент, а не копировать. Копирование выполняется только в том случае, если аргументом является значение lvalue.
3. @Gabrial: Обновил мой ответ более подробным объяснением. Помогает ли это?
4. Ваш второй пример кода не генерирует никаких предупреждений, если он скомпилирован как
g -Wall -std=c 0x
(версия g 4.5.2).5. @Tony: Предупреждения появляются, как только вы создаете экземпляр шаблона конструктора со списком инициализаторов, например
my_class foo({1.0, 2.0, 3.0}, {4.0, 5.0, 6.0});