Пересылка пакета параметров в constructor () не выполняется в g ++ 6.2.1 - PullRequest
0 голосов
/ 30 августа 2018

Как я могу преодолеть / обойти эту ошибку в g ++ - 6.2.1

Следующий код работает с g ++ - 7.3.0, но обновление компилятора мне не подходит. Так что я ищу магию SFINAE ... пробую немного, но пока не получилось ...

class Base {
public:
  Base(std::string str) : s(std::make_shared<std::string>(str)) {}
  Base(Base &&base) noexcept { s = std::move(base.s); }
  Base &operator=(Base &&i_base_actor) noexcept {
      s = std::move(i_base_actor.s);
      return *this;
  }
  virtual ~Base() = default;

private:
  std::shared_ptr<std::string> s;
};

// Derived
class Derived : public Base {
public:
   Derived() :Base("Derived") {}
   ~Derived() = default;
};

// Derived1
class Derived1 : public Base {
public:
   Derived1(int a) :Base("Derived1") {}
   ~Derived1() = default;
};

Функция обертки:

template<typename T, typename... Args>
T construct(Args&&... args) {
   return T(std::forward<Args>(args)...);
}

Main:

int main() {
  construct<Derived>();
  construct<Derived1>(100);
}

Ошибка в g ++

optional_params.derived.cc: In instantiation of ‘T construct(Args&& ...) [with T = Derived; Args = {}]’:
optional_params.derived.cc:42:22:   required from here
optional_params.derived.cc:37:19: error: use of deleted function ‘Derived::Derived(const Derived&)’
   return T(args...);
                   ^
optional_params.derived.cc:21:7: note: ‘Derived::Derived(const Derived&)’ is implicitly deleted because the default definition would be ill-formed:
 class Derived : public Base {
       ^~~~~~~
optional_params.derived.cc:21:7: error: use of deleted function ‘Base::Base(const Base&)’
optional_params.derived.cc:4:7: note: ‘Base::Base(const Base&)’ is implicitly declared as deleted because ‘Base’ declares a move constructor or move assignment operator
 class Base {
       ^~~~

1 Ответ

0 голосов
/ 30 августа 2018

Ваш код основан на гарантированном 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.
};

Но, поскольку это лишь минимальный воспроизводимый пример, возможно, что в полном коде фактически необходим явный деструктор. В этом случае избегать деструктора не вариант.

...