本文介绍了使用带有递归的std :: variant,而不使用boost :: recursive_wrapper的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想用C ++ 17 std::variant替换boost::variant并摆脱boost::recursive_wrapper,以在下面的代码中完全消除对boost的依赖.我该怎么办?

I'd like to replace boost::variants with C++17 std::variant and get rid of boost::recursive_wrapper, to remove dependency on boost completely in following code. How may I do that?

#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));
}

有两个通用函数:第一个函数reduce在运行时选择要返回哪个参数(此处为简洁起见,它仅返回第一个参数),第二个函数adapt将F类型的值转换为T类型的值.

There are two generic functions: first function reduce chooses at runtime which argument to return (here it just returns first argument for brevity), second function adapt converts a value of type F to a value of type T.

在此示例中,reduce返回类型为boost::variant<int, s>的对象,然后将其转换为类型为boost::variant<int, boost::recursive_wrapper<s> >的对象.

In this example reduce returns an object of type boost::variant<int, s> which is then converted to an object of type boost::variant<int, boost::recursive_wrapper<s> >.

推荐答案

boost::variant将进行堆分配,以便将其自身的一部分递归地定义为自身. (它还会在许多其他情况下进行堆分配,不确定多少)

boost::variant will heap allocate in order to have part of itself be recursively defined as itself. (It will also heap allocate in a number of other situations, uncertain how many)

std::variant不会. std::variant拒绝堆分配.

std::variant will not. std::variant refuses to heap allocate.

实际上没有办法在没有动态分配的情况下包含其自身的可能变体,因为如果静态声明,可以很容易地证明这种结构的大小是无限的. (您可以通过N次递归来编码整数N:没有固定大小的缓冲区可以容纳无限量的信息.)

There is no way to actually have a structure containing a possible variant of itself without a dynamic allocation, as such a structure can easily be shown to be infinite in size if statically declared. (You can encode the integer N by having N recursions of not-the-same: no fixed size buffer can hold an infinite amount of information.)

因此,等效的std::variant存储自身递归实例的某种占位符的智能指针.

As such, the equivalent std::variant stores a smart pointer of some kind placeholder of a recursive instance of itself.

这可能有效:

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交互.

It does mean that client code has to knowingly interact with the unique_ptr<s> and not the struct s directly.

如果要支持复制语义,则必须编写一个执行复制的value_ptr,并为其赋予等效于struct copy_s;的副本以实现该复制.

If you want to support copy semantics, you'll have to write a value_ptr that does copies, and give it the equivalent of struct copy_s; to implement that copy.

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的实时示例.

这篇关于使用带有递归的std :: variant,而不使用boost :: recursive_wrapper的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-30 00:12