Ваш код основан на гарантированном copy elision C ++ 17 в следующей строке:
template<typename T, typename... Args>
T construct(Args&&... args) {
return T(std::forward<Args>(args)...); // <----- copy elison
}
В основном, это говорит о том, что начиная с C ++ 17, компилятор не должен копировать T
в этом случае, и он обязан создавать его непосредственно в вызывающей программе. В C ++ 14 и более ранних версиях компилятор должен был убедиться, что конструктор перемещения (или копирования) доступен даже в тех случаях, когда он оптимизировал конструктор копирования. По-видимому, gcc-6.2.1 не поддерживал этот аспект C ++ 17, даже с флагом -std=c++17
.
Самый простой выход - добавить конструктор перемещения в производный класс:
Derived(Derived &&) noexcept = default;
Таким образом, компилятор C ++ 14 видит, что есть способ вернуть значение даже в гипотетическом случае, когда удаление копии не выполняется. Обратите внимание, что любой разумный компилятор C ++ 14 будет выполнять копирование, но он по-прежнему будет обеспечивать доступность конструкторов копирования или перемещения. Начиная с C ++ 17 такой тест не выполняется, так как в этом случае компилятор должен исключить копирование / перемещение.
Как уже упоминалось в разделе комментариев, еще одна возможность:
template<typename T, typename... Args>
T construct(Args&&... args) {
return {std::forward<Args>(args)...};
}
, который также создаст его непосредственно в вызывающей стороне, но только если конструктор T
не является явным.
В качестве альтернативы, другой комментарий предлагает избегать явного деструктора. Явный деструктор запрещает автоматическое создание конструкторов перемещения по умолчанию:
class Derived : public Base {
public:
Derived() :Base("Derived") {}
//~Derived() = default; <-- not really needed.
};
Но, поскольку это лишь минимальный воспроизводимый пример, возможно, что в полном коде фактически необходим явный деструктор. В этом случае избегать деструктора не вариант.