C — std::decay и std ::make_tuple — не удается преобразовать аргумент из ‘D’ в ‘D

#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>()};