boost::variant
sをC++ 17 std::variant
に置き換え、boost::recursive_wrapper
を削除して、次のコードでブーストへの依存を完全に削除したいと思います。どうすればいいですか?
#include <boost/variant.hpp>
#include <type_traits>
using v = boost::variant<int, boost::recursive_wrapper<struct s> >;
struct s
{
v val;
};
template<template <typename...> class R, typename T, typename ... Ts>
auto reduce(T t, Ts ... /*ts*/)
{
return R<T, Ts...>{t};
}
template<typename T, typename F>
T adapt(F f)
{
static_assert(std::is_convertible_v<F, T>, "");
return f;
}
int main()
{
int val1 = 42;
s val2;
auto val3 = adapt<v>(reduce<boost::variant>(val1, val2));
}
2つの一般的な関数があります。最初の関数reduce
は実行時に返す引数を選択し(ここでは簡潔にするために最初の引数を返すだけです)、2番目の関数adapt
はタイプFの値をタイプT。
この例では、reduce
はタイプboost::variant<int, s>
のオブジェクトを返し、タイプboost::variant<int, boost::recursive_wrapper<s> >
のオブジェクトに変換されます。
boost::variant
は、それ自体の一部を再帰的にそれ自体として定義するために、ヒープ割り当てを行います。 (他の多くの状況でもヒープ割り当てが発生しますが、その数は不明です)
std::variant
はしません。 std::variant
はヒープ割り当てを拒否します。
静的に宣言された場合、そのような構造はサイズが無限であることを簡単に示すことができるため、動的割り当てなしでそれ自体の可能なバリアントを含む構造を実際に持つ方法はありません。 (同じでないNの再帰を行うことで整数Nをエンコードできます。固定サイズのバッファーが無限の量の情報を保持することはできません。)
そのため、同等のstd::variant
は、それ自体の再帰インスタンスのある種のプレースホルダーのスマートポインターを格納します。
これはうまくいくかもしれません:
struct s;
using v = std::variant< int, std::unique_ptr<s> >;
struct s
{
v val;
~s();
};
inline s::~s() = default;
それが失敗した場合は、次を試してください。
struct destroy_s;
struct s;
using v = std::variant<int, std::unique_ptr<s, destroy_s> >;
struct s
{
v val;
~s();
};
struct destroy_s {
void operator()(s* ptr){ delete ptr; }
};
inline s::~s() = default;
これは、クライアントコードがunique_ptr<s>
と直接対話するのではなく、故意にstruct s
と対話する必要があることを意味します。
コピーセマンティクスをサポートする場合は、コピーを実行するvalue_ptr
を記述し、そのコピーを実装するためにstruct copy_s;
と同等のものを指定する必要があります。
template<class T>
struct default_copier {
// a copier must handle a null T const* in and return null:
T* operator()(T const* tin)const {
if (!tin) return nullptr;
return new T(*tin);
}
void operator()(void* dest, T const* tin)const {
if (!tin) return;
return new(dest) T(*tin);
}
};
template<class T, class Copier=default_copier<T>, class Deleter=std::default_delete<T>,
class Base=std::unique_ptr<T, Deleter>
>
struct value_ptr:Base, private Copier {
using copier_type=Copier;
// also typedefs from unique_ptr
using Base::Base;
value_ptr( T const& t ):
Base( std::make_unique<T>(t) ),
Copier()
{}
value_ptr( T && t ):
Base( std::make_unique<T>(std::move(t)) ),
Copier()
{}
// almost-never-empty:
value_ptr():
Base( std::make_unique<T>() ),
Copier()
{}
value_ptr( Base b, Copier c={} ):
Base(std::move(b)),
Copier(std::move(c))
{}
Copier const& get_copier() const {
return *this;
}
value_ptr clone() const {
return {
Base(
get_copier()(this->get()),
this->get_deleter()
),
get_copier()
};
}
value_ptr(value_ptr&&)=default;
value_ptr& operator=(value_ptr&&)=default;
value_ptr(value_ptr const& o):value_ptr(o.clone()) {}
value_ptr& operator=(value_ptr const&o) {
if (o && *this) {
// if we are both non-null, assign contents:
**this = *o;
} else {
// otherwise, assign a clone (which could itself be null):
*this = o.clone();
}
return *this;
}
value_ptr& operator=( T const& t ) {
if (*this) {
**this = t;
} else {
*this = value_ptr(t);
}
return *this;
}
value_ptr& operator=( T && t ) {
if (*this) {
**this = std::move(t);
} else {
*this = value_ptr(std::move(t));
}
return *this;
}
T& get() { return **this; }
T const& get() const { return **this; }
T* get_pointer() {
if (!*this) return nullptr;
return std::addressof(get());
}
T const* get_pointer() const {
if (!*this) return nullptr;
return std::addressof(get());
}
// operator-> from unique_ptr
};
template<class T, class...Args>
value_ptr<T> make_value_ptr( Args&&... args ) {
return {std::make_unique<T>(std::forward<Args>(args)...)};
}
実例 value_ptrの。