boost.variant 派生类型:不能使用复制构造函数

Posted

技术标签:

【中文标题】boost.variant 派生类型:不能使用复制构造函数【英文标题】:boost.variant derived type: cannot use copy constructor 【发布时间】:2017-02-24 11:00:29 【问题描述】:

我有一个派生自boost::variant<T, E> 的类型。我做了以下,我不能使用复制构造函数,我不知道为什么,一些SFINAE 似乎失败了。看起来boost::variant<T, E> 构造在继承的构造函数中将T 吞噬为ExpectedResult<T, E>。 我该如何修复它以使其正常工作并使用最简单的解决方案?

template <class T, class E>
class ExpectedResult : boost::variant<T, E> 
public:
    using boost::variant<T, E>::variant;
;

ExpectedResult<int,float> er;
ExpectedResult<int, float> er2 = er;


error: no type named 'type' in 'boost::enable_if<boost::mpl::and_<boost::mpl::not_<boost::is_same<Emotiv::Cortex::Utilities::ExpectedResult<int, float>, boost::variant<int, float> > >, boost::detail::variant::is_variant_constructible_from<const Emotiv::Cortex::Utilities::ExpectedResult<int, float> &, boost::mpl::l_item<mpl_::long_<2>, int, boost::mpl::l_item<mpl_::long_<1>, float, boost::mpl::l_end> > >, mpl_::bool_<true>, mpl_::bool_<true>, mpl_::bool_<true> >, void>'; 'enable_if' cannot be used to disable this declaration
        typename boost::enable_if<mpl::and_<
                                  ^~~~~~~~~~
note: in instantiation of member function 'boost::variant<int, float>::variant' requested here
    using boost::variant<T, E>::variant;
    while substituting deduced template arguments into function template 'ExpectedResult' [with T = Emotiv::Cortex::Utilities::ExpectedResult<int, float>]
    ExpectedResult<int, float> er2 = er;

【问题讨论】:

【参考方案1】:

Boost 变体有一个完美的转发构造函数。

您正在将其导入您的班级。

它通过检查self 是否正好是boost::variant 来防止消费self&amp;。你传递给它一个ExpectedResult&amp;

这让人困惑。

template <class T, class E>
struct ExpectedResult : boost::variant<T, E> 
  using boost::variant<T, E>::variant;
  ExpectedResult()=default;
  ExpectedResult(ExpectedResult const&)=default;
  ExpectedResult(ExpectedResult &&)=default;
  ExpectedResult(ExpectedResult & o):
    ExpectedResult( const_cast<ExpectedResult const&>(o) )
  
  ExpectedResult(ExpectedResult &&)=default;
  ExpectedResult(ExpectedResult const&& o):
    ExpectedResult( o ) // calls const& ctor
  
  ExpectedResult& operator=(ExpectedResult const&)=default;
  ExpectedResult& operator=(ExpectedResult &&)=default;
  ExpectedResult& operator=(ExpectedResult &) 
    return *this=const_cast<ExpectedResult const&>(o);
  
  ExpectedResult& operator=(ExpectedResult const&& o)
    return *this = o; // const& assign
  
;

我怀疑上述默认和手动编写的特殊成员函数可能会有所帮助。

为了完整起见,您还必须包含 volatile,将其分解为另一组。

我会小心添加我自己的完美转发构造函数以及使用父构造函数,因为这些更改在 C++17 中如何以可能破坏性的方式发生变化的规则。由于 C++17 中的重大变化,我目前对使用继承的构造函数持怀疑态度,我还没有完全理解。

另一种方法是避免继承构造,而是转发到变体。

template <class T, class E>
struct ExpectedResult : boost::variant<T, E> 
  using base=boost::variant<T, E>;
  ExpectedResult()=default;
  ExpectedResult(ExpectedResult const&)=default;
  ExpectedResult(ExpectedResult &&)=default;
  ExpectedResult(ExpectedResult &&)=default;
  ExpectedResult& operator=(ExpectedResult const&)=default;
  ExpectedResult& operator=(ExpectedResult &&)=default;

  template<class T0, class...Ts,
    class=std::enable_if_t<
      std::is_constructible<base, T0, Ts...>::value
      && (
        (sizeof...(ts)!=0)
        || !std::is_same<std::decay_t<T0>, ExpectedResult>::value
      )          
    >
  >
  ExpectedResult(T0&&t0, Ts&&...ts):
    base(std::forward<T0>(t0), std::forward<Ts>(ts)...)
  
;

这是一个完美的转发,它有所有的缺陷,但对于大多数用途来说足够接近。

initializer_list&lt;T&gt;, Ts&amp;&amp;... 构造函数可以使完美转发更加完美,所以ExpectedResult&lt;std::vector&lt;int&gt;, bool&gt; er 1,2,3,4 可以工作。

【讨论】:

是否应该将守卫行为视为错误?我发现所有必要的复制都过多。也许我应该提交一个错误。 @GermánDiago 该类型可能不是为了继承而设计的;但他们可以将一些 is_same 更改为 is_base_of 以使其适用于您的情况。我不知道这是否会导致其他问题...

以上是关于boost.variant 派生类型:不能使用复制构造函数的主要内容,如果未能解决你的问题,请参考以下文章

boost::variant 中持有的类的复制构造函数的问题

实现任何容器

使用 boost::mpl 获取 boost::variant 的类型索引

boost::variant 和多态性

如何在 boost::variant<T> 中存储引用而不是复制对象?

如何返回由 boost::variant 返回类型中包含的类型的子集组成的 boost::variant