Обоснование скрытого копирования и перемещения конструктора С++?

Мое понимание конструктора неявных копий С++ аналогично

T(T const& x) : 
    base1(x), base2(x) ... , 
    var1(x.var1), var2(x.var2)...
{}

Перемещение конструктора, копирование и перемещение также следует по аналогичной схеме.

Почему он не был определен как следующий?

T(T const& x) : 
    base1(static_cast<base1 const&>(x)),
    base2(static_cast<base2 const&>(x)) ... , 
    var1(x.var1), var2(x.var2)...
{}

Пример

У меня был класс, который имел неявный оператор конструктора/назначения copy/move, а также некоторые конструкторы преобразования. Я делегировал задание на некоторый класс реализации.

class common_work //common implementation of many work like classes
{
   common_work(common_work const&) = default;
   common_work(common_work&&) = default;// ... implicit constructors work for me.
   //a forwarding constructor which can take many work like objects
   template<class T, enable_if<work_like<T> > >
   common_work(T&& x) { ... }
};
class work1 //one of the implementation
{
   work1(work1 const& ) = default;
   work1(work1&& ) = default; ...
   common_work impl_;
};

Это было прекрасно, так как конструкторы work1 copy/move вызывали конструктор copy/move для common_work, а конструктор пересылки использовался другими конструкторами [не показанными в коде], которые преобразуются из другого типа work.

Тогда я думал унаследовать work1 от common_work для EBO и других причин. Итак, новый класс work1 выглядел как

class work1 : private common_work
{
   work1(work1 const& ) = default;
   work1(work1&& ) = default; ...
};

Но поскольку work1 является классом work_like, конструктор пересылки получает лучшее совпадение, поскольку конструктор copy/move для common_work требует static_cast от производной до базы.

ПРИМЕЧАНИЕ.

  • Существует аналогичный пример, приведенный Scott Meyers, где построение копирования вызывает конструктор пересылки в качестве конструктора копирования, требует добавления константы, а пересылка конструктор не требует. Но я думаю, что эта проблема возникает из-за неправильного дизайна класса, тогда как проблема здесь связана с аргументом, переданным базовому классу при неявной копировании/перемещении, не является точным совпадением.
  • Я не могу написать универсальный конструктор переадресации/назначение и удалить неявные, потому что удаленные функции также участвуют в разрешении перегрузки и вызывают ошибку, если они точно совпадают.
  • В настоящее время у меня есть решение сделать common_work как CRTP, т.е. тип производного класса, переданный как шаблонный аргумент, и в конструкторе пересылки отфильтровать его как enable_if<and_<work_like<T>,not_<is_same<T,Derived> > > >. В противном случае мне придется вручную писать copy/move constructor/assign для work1 и static_cast в явные классы базовых классов, что является ошибкой, ошибкой и опасностью обслуживания.

Ответ 1

Эта проблема обсуждалась на странице MSVС++ bugstracker несколько лет назад (поэтому, если она еще не исправлена, это известная проблема в MSVС++). В Стандарте говорится

  • ... база или элемент напрямую инициализируются с соответствующей базой или членом x.

Я тестировал различные компиляторы, когда читал bugreport, и все они "магически литые". Стандарт, похоже, очень тихий по очень подробным сведениям (также о категории значений и квалификаторах c/v), и просто говорит "с соответствующей базой... x", что ИМО делает гораздо больше смысла, если вы считаете, что это означает "с не х, а с соответствующей базой... х", а не тем, что вы передаете ему полный объект (я бы даже сказал, что это может иметь смысл именно так).