Ошибка компиляции при создании подкласса std::Optional

Я пытаюсь создать подкласс std:: optional в MS C++ 17 (VS2017), чтобы добавить поле сообщения в класс, но получаю ошибку компиляции

ошибка C2280: 'OptMsg<bool>::OptMsg(const OptMsg<bool> &)': попытка сослаться на удаленную функцию

Intellisense дает немного больше информации:

на функцию "OptMsg<T>::OptMsg(const OptMsg<bool> &) throw() [with T=bool]" (объявленную неявно) нельзя ссылаться - это удаленная функция

Что говорит мне о том, что у компилятора возникла проблема с моим конструктором копирования, ссылающимся на бросок удаленной функции? Я получаю сообщение об ошибке, возвращающее экземпляр из функции. Например.,

OptMsg<bool> foo()
{
    OptMsg<bool> res = false;
    return res; // <-- Getting compile error here
}

Вот мой класс. Любые идеи приветствуются!

template <class T>
class KB_MAPPING_ENGINE_API OptMsg : public std::optional<T>
{
public:
    constexpr OptMsg() noexcept
        : optional{}
        {}
    constexpr OptMsg(std::nullopt_t) noexcept
        : optional{}
        {}
    constexpr OptMsg(const T & other) noexcept
        : optional<T>{other}
        , m_Message{other.m_Message}
        {}
    constexpr explicit OptMsg(const T && other) noexcept
        : optional<T>{other}
        , m_Message{other.m_Message}
        {}
    OptMsg & operator = ( const OptMsg & other ) noexcept
    {
        if ( &other != this )
            m_Message = other.m_Message;
        return *this;
    }
    OptMsg && operator = ( const OptMsg && other )
    {
        if ( &other != this )
            m_Message = other.m_Message;
        return *this;
    }

    void SetMessage( const std::string & message ) { m_Message = message; }

    std::string GetMessage() { return m_Message; }

private:
    std::string m_Message;
};

person buttonsrtoys    schedule 17.07.2019    source источник
comment
Я не думаю, что std::optional действительно предназначен для наследования. Я думаю, что композиция была бы лучшим выбором дизайна здесь.   -  person Some programmer dude    schedule 17.07.2019
comment
проверьте stackoverflow.com/questions/31264984/   -  person nivpeled    schedule 17.07.2019


Ответы (2)


Что говорит мне о том, что у компилятора возникла проблема с моим конструктором копирования, ссылающимся на бросок удаленной функции?

Нет, функции throw() нет. Эта нотация является способом объявления того, что функция ничего не выбрасывает до C++11. В настоящее время рекомендуется noexcept, но, видимо, Microsoft еще не догнала его...

Вместо этого ошибка сообщает вам, что вы пытаетесь вызвать конструктор копирования (в отмеченной вами строке), но в вашем классе его нет!

Почему его нет? Это здесь

constexpr OptMsg(const T & other) noexcept
    : optional<T>{other}
    , m_Message{other.m_Message}
    {}

похоже, что он предназначен для конструктора копирования. Однако это не потому, что аргумент const T&. Конструктору копирования требуется const OptMsg&.

Обычно конструктор копирования объявляется автоматически. Здесь этого не произошло, потому что вы явно объявили operator=(const OptMsg&). Следовательно, в сообщении об ошибке упоминается, что ваш конструктор копирования является «удаленной функцией».

Как это исправить?

Либо правильно объявите свой конструктор копирования, либо удалите оператор присваивания. Компилятор сгенерирует его для вас. Но обратите внимание, что ваши текущие реализации операторов присваивания (копирование и перемещение) присваивают только сообщение, но не сам optional. Это предназначено? Было бы очень неожиданное поведение... Если так и задумано, то нужно объявить все самому (но правильно!).

Но предполагая, что это не предназначено, и далее предполагая, что ваши операции копирования должны просто тривиально копировать весь экземпляр (сообщение + базовый класс), тогда автоматически сгенерированные конструкторы и операторы присваивания делают именно то, что вы хотите, нет необходимости писать их самостоятельно. Однако вы можете записать это в свой класс, чтобы все сразу увидели, что вы используете сгенерированные компилятором:

constexpr OptMsg() = default;

constexpr OptMsg(const OptMsg & other) = default;
constexpr OptMsg(OptMsg && other) = default;
OptMsg & operator = ( const OptMsg & other ) = default;
OptMsg & operator = ( OptMsg && other ) = default;

Обновлять:

Обратите внимание, что конструктор перемещения и оператор присваивания перемещения требуют OptMsg&& в качестве аргумента. Вы продолжаете иметь const OptMsg&&. Следовательно, ваше сообщение об ошибке

"оператор = (const OptMsg &&)': не является специальной функцией-членом, которая может быть по умолчанию

Также взгляните на правило нуля (спасибо @Caleth).

person sebrockm    schedule 17.07.2019
comment
Спасибо! Единственные изменения, которые мне были нужны для решения = по умолчанию, заключались в том, чтобы добавить обратно в конструктор constexpr OptMsg(const T& value ): OptMsg::Optional{value} {}, чтобы я мог OptMsg‹bool› res(false);. Кроме того, MS выдает оператор ошибки компиляции =(const OptMsg‹T› &&)': не является специальной функцией-членом, которая может быть установлена ​​по умолчанию, поэтому я свернул свою собственную. - person buttonsrtoys; 17.07.2019
comment
См. также: нулевое правило - person Caleth; 17.07.2019
comment
@buttonsrtoys, пожалуйста, ознакомьтесь с моим сообщением об ошибке. Не надо накатывать свою, просто опять не та подпись :) - person sebrockm; 17.07.2019
comment
@sebrockm Спасибо за постоянное напоминание. Я вставил ваш оператор присваивания перемещения и получил ту же ошибку. Оказывается, компилятору не понравился возвращаемый тип OptMsg&&. OptMsg& сработало! - person buttonsrtoys; 17.07.2019
comment
@buttonsrtoys О, абсолютно! Я скопировал его у вас и пропустил настройку типа возврата. Это должно быть действительно OptMsg&. - person sebrockm; 17.07.2019

Я обнаружил некоторые проблемы, такие как:

constexpr explicit OptMsg(const T && other) noexcept
    : optional<T>{other}
    , m_Message{other.m_Message}
    {}

я полагаю, вы имеете в виду

constexpr explicit OptMsg( OptMsg&& other) noexcept
    : optional<T>{std::forward<OptMsg>(other)}
    , m_Message{other.m_Message}
    {}

Два замечания здесь: Перемещение не является постоянным! Исходные данные могут быть изменены, чтобы указать, что право собственности на данные было перемещено! Вы должны использовать std::forward для пересылки ссылки rvalue в конструктор перемещения базового класса.

Также ваш:

std::string GetMessage() const { return m_Message; }

должен быть помечен const!

Следующее:

constexpr OptMsg( const T& value ): OptMsg::optional{ value }{}

не может быть помечен как explicit, поскольку он предназначен для преобразования типов из bool->OptMsg

person Klaus    schedule 17.07.2019