почему этот код компилируется, когда нет очевидного соответствия конструктора?

Пожалуйста, рассмотрите следующий код:

class Foo {
public:
explicit Foo(double) {}
};

Foo * test();
Foo * test() {
return new Foo(Foo(1.0));   // (1)
}

Мой вопрос касается строки (1). Это очень похоже на ошибку, которая заняла у меня некоторое время, чтобы выследить. Я не заметил, что тип был указан дважды из-за ошибки копирования / вставки. Правильная строка очевидно:

  return new Foo(1.0);

Интересно, что это изменение также компилируется без предупреждения:

  return new Foo(Foo(Foo(Foo(1.0))));

Почему эти примеры компилируются без предупреждений с помощью clang, даже если -Wall -Weverything флаги? Почему Foo::Foo(double) принять экземпляр Foo в качестве действительного double аргумент? Это какое-то своеобразное поведение оператора нового?

Мой оригинальный код был в более широком контексте и протестирован с двумя компиляторами на основе LLVM-3. Оба скомпилированы без предупреждений или ошибок. С одним, код фактически функционировал, как я ожидал, и на самом деле я не осознавал, что в течение некоторого времени была ошибка. С другой стороны, экземпляр Foo вел себя очень странно — я не могу описать его должным образом — это было так, как если бы более поздняя копия возвращенного указателя «волшебным образом» стала отличаться от оригинала, что привело к несовпадению состояний между двумя взаимодействующими объекты, которые должны были содержать эквивалентные указатели на общий Foo, но по какой-то причине содержали разные значения после присваивания. Пока я не понимаю, что здесь происходит, это кажется действительно странным!

Интересно, что следующие компиляторы с обоими компиляторами:

class Foo { public: explicit Foo(double) {} };
class Bar { public: explicit Bar(double) {} };

Foo * testFoo() { return new Foo(Foo(1.0)); }
Bar * testBar() { return new Bar(Bar(1.0)); }

Но следующая версия не делает:

Foo * testFooBar() { return new Foo(Bar(1.0)); }

2

Решение

Компилятор автоматически генерирует конструктор копирования Foo(const Foo&) и, возможно, также перемещать конструктор в зависимости от точной версии / настроек Foo(Foo&&), Это не имеет ничего общего с оператором new или магией указателя. Ваш код просто вызывает совершенно нормальный конструктор копирования / перемещения, определенный для вас компилятором. Это все. Такое поведение предписано стандартом. Некодируемый класс (по крайней мере, в оригинальной версии Стандарта) практически бесполезен.

Если вам не нужны автоматически сгенерированные конструкторы копирования, обычная техника — определить их как удаленные или частные.

Также обратите внимание, что компилятор в некоторых случаях имеет право фактически исключать целые объекты из вашей программы, хотя обычно это не должно быть. Если вы выполняете переходы с указателем на Foo, установленным в конструкторе Foo, вы должны строго обрабатывать их во всех конструкторах и деструкторах, то есть иметь необходимость писать свои собственные конструкторы копирования / перемещения, деструкторы и операторы присваивания, иначе вы придете обрезка, когда компилятор исключает объект.

7

Другие решения

Конструктор, который вы наблюдаете, является конструктором копирования. У каждого класса есть конструктор копирования. Если вы не объявляете конструктор копирования в своем определении класса, конструктор копирования неявно объявлено для тебя.

Конкретная сигнатура неявно объявленного конструктора копирования зависит от определения вашего класса, то есть от основ и членов — как правило, имеет вид X::X(X const &), но это может быть X::X(X &) или же X::X(X volatile &) если необходимо. Спецификации исключений максимально жесткие. Определение неявно объявленного конструктора копирования обычно состоит из копирования баз и членов, но при определенных обстоятельствах его можно определить как удаленное (например, если вы объявляете конструктор перемещения).

(В будущих версиях C ++, если вы объявите оператор присваивания копирования или перемещения, неявно объявленный конструктор копирования будет определен как удаленный. В C ++ 11 и C ++ 14 он используется по умолчанию.)

2

По вопросам рекламы [email protected]