为什么我不能 std::optional<T> 其中 T 是抽象的?

Why can't I have std::optional<T> where T is abstract?

这不起作用:

struct Type {
    virtual bool func(const std::string& val) const noexcept = 0;
}

// in main
optional<Type> = some_function_returning_optional_type();

并失败并显示错误消息:

error: cannot declare field 'std::experimental::fundamentals_v1::_Optional_base<Type, false>::<anonymous union>::_M_payload' to be of abstract type 'Type'

Type 更改为具有非纯函数是可行的,但在这种情况下不合适,因为我的代码中不能有 Type 的实例,只有 类继承自它应该可以存在。

std::optional<T> 就地存储它的值 - 因此它需要知道 T 的大小才能正常工作,并且 T 必须是可以实例化的具体类型。您可以将 std::optional<T> 视为:

template <typename T>
struct optional
{
    std::aligned_storage_t<sizeof(T), alignof(T)> _data;
    bool _set;
};

抽象类型表示接口 - 需要多态性和某种间接才能使用抽象类型。 std::optional 没有设计任何间接寻址。

你提出的可选遗嘱当然可行,但不得不写出来会冒犯我

x.value()->do_something();

而且我担心用户可能会做一些愚蠢的事情:

x.value().reset();  // now what?

我们可以通过使用包装器实现非多态接口的多态。

这是一种方法:

#include <optional>
#include <iostream>

// the Foo interface/base class
struct Foo
{
    virtual ~Foo() = default;
    virtual Foo* clone() const { return new Foo(*this); }

    virtual void do_something() {
        std::cout << "something Fooey\n";
    }
};

// a service for managing Foo and classes derived from Foo
struct FooService
{
    template<class Arg>
    Foo* clone(Arg&& arg)
    {
        using d_type = std::decay_t<Arg>; 
        return new d_type(std::forward<Arg>(arg));
    }

    template<class Arg>
    Foo* clone(Foo* arg)
    {
        return arg->clone();
    }

    Foo* release(Foo*& other) noexcept
    {
        auto tmp = other;
        other = nullptr;
        return tmp;
    }
};

// implement the Foo interface in terms of a pimpl
template<class Holder>
struct BasicFoo
{

    decltype(auto) do_something() {
        return get().do_something();
    }



private:
    Foo& get() noexcept { return static_cast<Holder*>(this)->get_impl(); }
    Foo const& get() const noexcept { return static_cast<Holder const*>(this)->get_impl(); }
};


// a type for holding anything derived from a Foo
// can be initialised by anything Foo-like and handles copy/move correctly
struct FooHolder : BasicFoo<FooHolder>
{
    template
    <
        class Arg,
        std::enable_if_t
        <
            std::is_base_of_v<Foo, std::decay_t<Arg>>
        >* = nullptr
    >
    FooHolder(Arg&& arg)
    : service_()
    , ptr_(service_.clone(std::forward<Arg>(arg)))
    {}

    FooHolder(FooHolder const& other)
    : service_()
    , ptr_(other.ptr_->clone())
    {
    }

    FooHolder(FooHolder && other) noexcept
    : service_()
    , ptr_(service_.release(other.ptr_))
    {
    }

    FooHolder& operator=(FooHolder const& other)
    {
        auto tmp = other;
        std::swap(ptr_, tmp.ptr_);
        return *this;
    }

    FooHolder& operator=(FooHolder && other) noexcept
    {
        auto tmp = std::move(other);
        std::swap(ptr_, tmp.ptr_);
        return *this;
    }

    ~FooHolder()
    {
        delete ptr_;
    }

    Foo& get_impl() noexcept { return *ptr_; }
    Foo const& get_impl() const noexcept { return *ptr_; }

    FooService service_;
    Foo* ptr_;
};

// now we can supply as many overrides of Foo as we like    
struct Bar : Foo
{
    virtual Foo* clone() const { return FooService().clone(*this); }

    virtual void do_something() {
        std::cout << "something Barey\n";
    }

};

int main()
{
    std::optional<FooHolder> opt;

    // note that we're initialising cleanly    
    opt = Bar {};

    // and we don't expose the pointer so the user can't
    // destroy the pimpl accidentally
    opt.value().do_something();
}