Почему у меня не может быть std :: option & lt; T & gt; где Т абстрактно?

Это не работает:

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 в моем коде должны существовать только классы, которые наследуют его.

1

Решение

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 не имеет никакого косвенного влияния по дизайну.

3

Другие решения

Ваше предложение по желанию, конечно, будет работать, но мне было бы обидно писать

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();
}
0

По вопросам рекламы [email protected]