Отключить конструктор пересылки std::Optional

Я расширил QDataStream с помощью оператора преобразования шаблона, чтобы поток данных загружался из самого себя и преобразовывался в любой поддерживаемый тип, как таковой:

class ConvertibleQDataStream : public QDataStream
{
public:

    using QDataStream::QDataStream;

    template <class T>
    explicit operator T ()
    {
        T t;
        *this >> t;
        return t;
    }
};

И можно добавить поддержку типов, не поддерживаемых QDataStream, перегрузив оператор >> как таковой:

template <class T>
ConvertibleQDataStream&
operator >> (ConvertibleQDataStream& ds, std::vector<T>& v)
{
    //Called for std::vector's.
    return ds;
}

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

class Bar
{
public:

    Bar(ConvertibleQDataStream&);
};

class Foo
{
    int mInt;
    std::vector<double> mVector;
    Bar mBar;

public:

    Foo(ConvertibleQDataStream& ds) :
        mInt(ds),     //Calls operator >> for int and converts to int 
        mVector(ds),  //Calls operator >> for std::vector<T> and converts to std::vector<T>
        mBar(ds)      //Plain constructor call 
    {}
};

Это прекрасно работает, за исключением случаев, когда участник является std::optional. Конструктор пересылки std::optional вызывается вместо оператора преобразования шаблона ConvertibleQDataStream:

template <class T>
ConvertibleQDataStream&
operator >> (ConvertibleQDataStream& ds, std::optional<T>& o)
{
    //Never called :(
    return ds;
}

class Qux
{
    std::optional<Bar> mOptional;

public:

    Foo(ConvertibleQDataStream& ds) :
        mOptional(ds) //calls Bar::Bar(ConvertibleQDataStream&) rather then operator >> for std::optional<T> due to forwarding c'tor.
    {}
};

Можно ли отключить конструктор пересылки std::optional? Или другой обходной путь для этого.


person Unimportant    schedule 24.05.2019    source источник
comment
Что не так с Foo(ConvertibleQDataStream& ds){ ds >> mOptional; }?   -  person alter igel    schedule 24.05.2019
comment
@alterigel: класс может иметь конструируемые члены не по умолчанию. Они должны быть инициализированы в списке членов-инициализаторов. Поскольку порядок, в котором данные извлекаются из потока, важен, некоторые элементы нельзя переместить в тело конструктора.   -  person Unimportant    schedule 24.05.2019
comment
@Неважно, вы имеете в виду, что хотите, чтобы ConvertibleQDataStream::operator T() вызывался с T=std::optional<Bar>?   -  person alter igel    schedule 24.05.2019
comment
@alterigel Да, это работает для всех других типов, но мешают конструкторы пересылки.   -  person Unimportant    schedule 24.05.2019
comment
стандартное преобразование получило предпочтение перед пользовательским.   -  person Swift - Friday Pie    schedule 24.05.2019


Ответы (1)


Это не проблема с option, это проблема вашего дизайна, где mOptional конструируется из ConvertibleQDataStream.

Правила преобразования C++ могут быть кошмаром, и в этом случае, скорее всего, их следует избегать, предоставляя явные операторы получения.

class ConvertibleQDataStream : public QDataStream
{
public:
    using QDataStream::QDataStream;

    template <class T>
     T Get() const
    {
        T t;
        *this >> t;
        return t;
    }
};

class Qux
{
    std::optional<Bar> mOptional;

public:

    Foo(ConvertibleQDataStream& ds) :
        mOptional(ds.Get<std::optional<Bar>>())
    {}
};
person IdeaHat    schedule 24.05.2019
comment
Они не так уж и кошмарны, поскольку предпочтение пользовательского преобразования может привести к #define true false хаосу. - person Swift - Friday Pie; 24.05.2019
comment
С идеальными конструкторами переадресации они вызывают всевозможные сумасшествия, даже в stl. std::Optional‹bool› ведет себя особенно плохо: std::Optional‹bool› x; // пустой std::Optional‹bool› y(x); // как вы думаете, что здесь происходит? - person IdeaHat; 24.05.2019
comment
не уверен, что вы имеете в виду. они оба пусты... потому что x был пуст? Разве он не использует конструктор копирования? Другие конструкторы являются явными или условно явными. - person Swift - Friday Pie; 24.05.2019
comment
@Swift-FridayPie y на самом деле содержит ложь. std::optionl неявно преобразуется в bool. - person IdeaHat; 18.03.2020