#c #c 11 #templates #tuples
#c #c 11 #шаблоны #кортежи
Вопрос:
У меня есть этот код:
template <typename T>
void someFunction(){
typedef std::decay<T>::type D;
D val = GetValue<D>();
std::tuple<D> t = std::make_tuple<D>(val);
//... tuple is stored outside this scope in global variable
}
GetValue<D>()
возвращает значение из словаря на основе typeid(D)
, оно работает правильно, и я возвращаюсь MyData
к D
— я проверил это, если я закомментирую следующую проблемную строку, см. Проблему ниже.
Если я скомпилирую это, для T = const MyData amp;
я получу это
error C2664: 'std::tuple<MyData > std::make_tuple<D>(D amp;amp;)': cannot convert argument 1 from 'D' to 'D amp;amp;'
Почему возникает эта ошибка и как ее удалить? Я хочу хранить в своем кортеже только значения, отличные от ref, даже если шаблон T имеет значение refd’.
Я могу скомпилировать его, используя std::make_tuple<D>(std::forward<D>(val))
, но он вызывает мой move ctor, и я хочу вызвать copy ctor, потому что val
уничтожается в конце метода, и поэтому его внутренние данные и move ctor просто переместили их, поэтому они исчезли.
Комментарии:
1. Я вижу желание избежать случайного вызова конструктора копирования и перемещения, но ваш конструктор перемещения должен аннулировать исходный объект, чтобы предотвратить эту проблему. Если это невозможно, вам, вероятно, следует просто удалить его, поскольку почти любое использование конструктора перемещения приведет к сбою.
Ответ №1:
Вы никогда не должны передавать явные параметры шаблона в make_tuple
и make_pair
. Их сила заключается именно в их способности выводить типы, а также их разложение и разворачивание ссылочных оболочек.
Должно работать следующее:
std::tuple<D> t = std::make_tuple(std::move(val)); // forward is superfluous here
или, еще лучше:
auto t = std::make_tuple(GetValue<D>());
Комментарии:
1. Кроме того, замена
std::tuple<D>
наauto
может быть хорошей идеей.2. @krzaq Он компилируется нормально, но если я пошагово выполняю код построчно, я все равно оказываюсь в move ctor (
MyData::MyData(MyDataamp;amp; other){}
)3. @MartinPerry: Если сначала вызывается конструктор копирования, то после этого можно вызвать конструктор перемещения (поскольку вы просто перемещаете копию). Он должен вызывать конструктор копирования при сохранении
GetValue<D>()
вval
.4. @Guvante
GetValue
вызывает copy ctor (create A), затем tuple вызывает move ctor (create B) . но после этого вызывается copy (A) dtor, и он принимает данные в move (B) вместе с ними5. @MartinPerry Я думаю, что это зависит от способности вашего компилятора выполнять (N) RVO, а это, в свою очередь, зависит от реализации
GetValue
. Что произойдет, если вы просто напишетеstd::tuple<D> t{GetValue<D>()};