【问题标题】:How to implement a boost::variant derived-class?如何实现 boost::variant 派生类?
【发布时间】:2012-11-24 21:48:02
【问题描述】:

我已经尝试了几个小时来编写一个派生自 boost::variant 的类。但是我不明白是什么问题(我不明白编译错误是什么意思)。

实现干净的boost::variant派生类的规则是什么?

#include <boost/variant.hpp>

class MyVariant : public boost::variant<char,bool>
{
public:    
       MyVariant ()          : boost::variant<char,bool>( ) {}

       template <typename T>
       MyVariant(      T& v) : boost::variant<char,bool>(v) {}

       template <typename T>
       MyVariant(const T& v) : boost::variant<char,bool>(v) {}
};

int main ()
{
      MyVariant a;
      MyVariant b = a;        //compilation error
  //  MyVariant c = MyVariant();
  //  MyVariant d (true);
  //  MyVariant e ('E');
}

我为什么要使用继承?(编辑以向@zaufi 提供更多详细信息)

  • 我想要一个空状态
  • 我想接受const char*string
  • 我想接受intlong
  • 我想给enum类型

例如,在伪 C++ 代码中,我希望:

class MyVariant : public boost::variant<char,bool,long,std::string>
{
  typedef boost::variant<char,bool,long,std::string> super;

public:    

  // I know here I should specialize templeted constructors
  // but I is more clear like that, isn't it?    
  MyVariant()              : super('e')             {} //empty -> char
  MyVariant(char        c) : super(std::string(1,c)){} //char  -> string
  MyVariant(const char* s) : super(std::string(s) ) {} //char* -> string
  MyVariant(int         v) : super(long       (v) ) {} //TODO boundaries    
  /* other constructors ... */

  enum Type
  {
    NONE,  //my empty state = char type
    BOOL,
    LONG,
    STRING
  };

  Type type() const { return (Type) which(); }    
};

基本的sn-p代码(题主)已经在不同平台上测试过

  • boost v1.33 + GCC 4.1 (Linux)
  • 提升 v1.52 + GCC 4.7 (MinGW)
  • boost v1.52 + Visual C++ 2010 (v10)

以下两个版本的 GCC 错误 (如果它打扰某人,我可以删除两者之一......)


$ g++ --version
g++ (GCC) 4.1.2 20080704 (Red Hat 4.1.2-52)
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ g++ myVariant.cpp
/usr/include/boost/variant/variant.hpp: In constructor 'boost::variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>::variant(T&) [with T = MyVariant, T0_ = char, T1 = bool, T2 = boost::detail::variant::void_, T3 = boost::detail::variant::void_, T4 = boost::detail::variant::void_, T5 = boost::detail::variant::void_, T6 = boost::detail::variant::void_, T7 = boost::detail::variant::void_, T8 = boost::detail::variant::void_, T9 = boost::detail::variant::void_, T10 = boost::detail::variant::void_, T11 = boost::detail::variant::void_, T12 = boost::detail::variant::void_, T13 = boost::detail::variant::void_, T14 = boost::detail::variant::void_, T15 = boost::detail::variant::void_, T16 = boost::detail::variant::void_, T17 = boost::detail::variant::void_, T18 = boost::detail::variant::void_, T19 = boost::detail::variant::void_]':
myVariant.cpp:10:   instantiated from 'MyVariant::MyVariant(T&) [with T = MyVariant]'
myVariant.cpp:19:   instantiated from here
/usr/include/boost/variant/variant.hpp:1348: error: call of overloaded 'convert_construct(MyVariant&, long int)' is ambiguous
/usr/include/boost/variant/variant.hpp:1262: note: candidates are: void boost::variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>::convert_construct(T&, int, mpl_::false_) [with T = MyVariant, T0_ = char, T1 = bool, T2 = boost::detail::variant::void_, T3 = boost::detail::variant::void_, T4 = boost::detail::variant::void_, T5 = boost::detail::variant::void_, T6 = boost::detail::variant::void_, T7 = boost::detail::variant::void_, T8 = boost::detail::variant::void_, T9 = boost::detail::variant::void_, T10 = boost::detail::variant::void_, T11 = boost::detail::variant::void_, T12 = boost::detail::variant::void_, T13 = boost::detail::variant::void_, T14 = boost::detail::variant::void_, T15 = boost::detail::variant::void_, T16 = boost::detail::variant::void_, T17 = boost::detail::variant::void_, T18 = boost::detail::variant::void_, T19 = boost::detail::variant::void_]
/usr/include/boost/variant/variant.hpp:1321: note:                 void boost::variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>::convert_construct(boost::variant<U0, U1, U2, U3, U4, U5, U6, U7, U8, U9, U10, U11, U12, U13, U14, U15, U16, U17, U18, U19>&, long int) [with U0 = char, U1 = bool, U2 = boost::detail::variant::void_, U3 = boost::detail::variant::void_, U4 = boost::detail::variant::void_, U5 = boost::detail::variant::void_, U6 = boost::detail::variant::void_, U7 = boost::detail::variant::void_, U8 = boost::detail::variant::void_, U9 = boost::detail::variant::void_, U10 = boost::detail::variant::void_, U11 = boost::detail::variant::void_, U12 = boost::detail::variant::void_, U13 = boost::detail::variant::void_, U14 = boost::detail::variant::void_, U15 = boost::detail::variant::void_, U16 = boost::detail::variant::void_, U17 = boost::detail::variant::void_, U18 = boost::detail::variant::void_, U19 = boost::detail::variant::void_, T0_ = char, T1 = bool, T2 = boost::detail::variant::void_, T3 = boost::detail::variant::void_, T4 = boost::detail::variant::void_, T5 = boost::detail::variant::void_, T6 = boost::detail::variant::void_, T7 = boost::detail::variant::void_, T8 = boost::detail::variant::void_, T9 = boost::detail::variant::void_, T10 = boost::detail::variant::void_, T11 = boost::detail::variant::void_, T12 = boost::detail::variant::void_, T13 = boost::detail::variant::void_, T14 = boost::detail::variant::void_, T15 = boost::detail::variant::void_, T16 = boost::detail::variant::void_, T17 = boost::detail::variant::void_, T18 = boost::detail::variant::void_, T19 = boost::detail::variant::void_]
/usr/include/boost/variant/variant.hpp:1330: note:                 void boost::variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>::convert_construct(const boost::variant<U0, U1, U2, U3, U4, U5, U6, U7, U8, U9, U10, U11, U12, U13, U14, U15, U16, U17, U18, U19>&, long int) [with U0 = char, U1 = bool, U2 = boost::detail::variant::void_, U3 = boost::detail::variant::void_, U4 = boost::detail::variant::void_, U5 = boost::detail::variant::void_, U6 = boost::detail::variant::void_, U7 = boost::detail::variant::void_, U8 = boost::detail::variant::void_, U9 = boost::detail::variant::void_, U10 = boost::detail::variant::void_, U11 = boost::detail::variant::void_, U12 = boost::detail::variant::void_, U13 = boost::detail::variant::void_, U14 = boost::detail::variant::void_, U15 = boost::detail::variant::void_, U16 = boost::detail::variant::void_, U17 = boost::detail::variant::void_, U18 = boost::detail::variant::void_, U19 = boost::detail::variant::void_, T0_ = char, T1 = bool, T2 = boost::detail::variant::void_, T3 = boost::detail::variant::void_, T4 = boost::detail::variant::void_, T5 = boost::detail::variant::void_, T6 = boost::detail::variant::void_, T7 = boost::detail::variant::void_, T8 = boost::detail::variant::void_, T9 = boost::detail::variant::void_, T10 = boost::detail::variant::void_, T11 = boost::detail::variant::void_, T12 = boost::detail::variant::void_, T13 = boost::detail::variant::void_, T14 = boost::detail::variant::void_, T15 = boost::detail::variant::void_, T16 = boost::detail::variant::void_, T17 = boost::detail::variant::void_, T18 = boost::detail::variant::void_, T19 = boost::detail::variant::void_]

$ g++ --version
g++.exe (GCC) 4.7.2
Copyright (C) 2012 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ g++ myVariant.cpp -I /c/.../include/
In file included from c:/.../include/boost/variant.hpp:17:0,
                 from myVariant.cpp:1:
c:/.../include/boost/variant/variant.hpp: In instantiation of 'boost::variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>::variant(T&) [with T = MyVariant; T0_ = char; T1 = bool; T2 = boost::detail::variant::void_; T3 = boost::detail::variant::void_; T4 = boost::detail::variant::void_; T5 = boost::detail::variant::void_; T6 = boost::detail::variant::void_; T7 = boost::detail::variant::void_; T8 = boost::detail::variant::void_; T9 = boost::detail::variant::void_; T10 = boost::detail::variant::void_; T11 = boost::detail::variant::void_; T12 = boost::detail::variant::void_; T13 = boost::detail::variant::void_; T14 = boost::detail::variant::void_; T15 = boost::detail::variant::void_; T16 = boost::detail::variant::void_; T17 = boost::detail::variant::void_; T18 = boost::detail::variant::void_; T19 = boost::detail::variant::void_]':
myVariant.cpp:9:66:   required from 'MyVariant::MyVariant(T&) [with T = MyVariant]'
myVariant.cpp:18:25:   required from here
c:/.../include/boost/variant/variant.hpp:1406:9: error: call of overloaded convert_construct(MyVariant&, long int)' is ambiguous
c:/.../include/boost/variant/variant.hpp:1406:9: note: candidates are:
c:/.../include/boost/variant/variant.hpp:1316:10: note: void boost::variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>::convert_construct(T&, int, mpl_::false_) [with T = MyVariant; T0_ = char; T1 = bool; T2 = boost::detail::variant::void_; T3 = boost::detail::variant::void_; T4 = boost::detail::variant::void_; T5 = boost::detail::variant::void_; T6 = boost::detail::variant::void_; T7 = boost::detail::variant::void_; T8 = boost::detail::variant::void_; T9 = boost::detail::variant::void_; T10 = boost::detail::variant::void_; T11 = boost::detail::variant::void_; T12 = boost::detail::variant::void_; T13 = boost::detail::variant::void_; T14 = boost::detail::variant::void_; T15 = boost::detail::variant::void_; T16 = boost::detail::variant::void_; T17 = boost::detail::variant::void_; T18 = boost::detail::variant::void_; T19 = boost::detail::variant::void_; mpl_::false_ = mpl_::bool_<false>]
c:/.../include/boost/variant/variant.hpp:1376:10: note: void boost::variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>::convert_construct(boost::variant<U0, U1, U2, U3, U4, U5, U6, U7, U8, U9, U10, U11, U12, U13, U14, U15, U16, U17, U18, U19>&, long int) [with U0 = char; U1 = bool; U2 = boost::detail::variant::void_; U3 = boost::detail::variant::void_; U4 = boost::detail::variant::void_; U5 = boost::detail::variant::void_; U6 = boost::detail::variant::void_; U7 = boost::detail::variant::void_; U8 = boost::detail::variant::void_; U9 = boost::detail::variant::void_; U10 = boost::detail::variant::void_; U11 = boost::detail::variant::void_; U12 = boost::detail::variant::void_; U13 = boost::detail::variant::void_; U14 = boost::detail::variant::void_; U15 = boost::detail::variant::void_; U16 = boost::detail::variant::void_; U17 = boost::detail::variant::void_; U18 = boost::detail::variant::void_; U19 = boost::detail::variant::void_; T0_ = char; T1 = bool; T2 = boost::detail::variant::void_; T3 = boost::detail::variant::void_; T4 = boost::detail::variant::void_; T5 = boost::detail::variant::void_; T6 = boost::detail::variant::void_; T7 = boost::detail::variant::void_; T8 = boost::detail::variant::void_; T9 = boost::detail::variant::void_; T10 = boost::detail::variant::void_; T11 = boost::detail::variant::void_; T12 = boost::detail::variant::void_; T13 = boost::detail::variant::void_; T14 = boost::detail::variant::void_; T15 = boost::detail::variant::void_; T16 = boost::detail::variant::void_; T17 = boost::detail::variant::void_; T18 = boost::detail::variant::void_; T19 = boost::detail::variant::void_]
c:/.../include/boost/variant/variant.hpp:1385:10: note: void boost::variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>::convert_construct(const boost::variant<U0, U1, U2, U3, U4, U5, U6, U7, U8, U9, U10, U11, U12, U13, U14, U15, U16, U17, U18, U19>&, long int) [with U0 = char; U1 = bool; U2 = boost::detail::variant::void_; U3 = boost::detail::variant::void_; U4 = boost::detail::variant::void_; U5 = boost::detail::variant::void_; U6 = boost::detail::variant::void_; U7 = boost::detail::variant::void_; U8 = boost::detail::variant::void_; U9 = boost::detail::variant::void_; U10 = boost::detail::variant::void_; U11 = boost::detail::variant::void_; U12 = boost::detail::variant::void_; U13 = boost::detail::variant::void_; U14 = boost::detail::variant::void_; U15 = boost::detail::variant::void_; U16 = boost::detail::variant::void_; U17 = boost::detail::variant::void_; U18 = boost::detail::variant::void_; U19 = boost::detail::variant::void_; T0_ = char; T1 = bool; T2 = boost::detail::variant::void_; T3 = boost::detail::variant::void_; T4 = boost::detail::variant::void_; T5 = boost::detail::variant::void_; T6 = boost::detail::variant::void_; T7 = boost::detail::variant::void_; T8 = boost::detail::variant::void_; T9 = boost::detail::variant::void_; T10 = boost::detail::variant::void_; T11 = boost::detail::variant::void_; T12 = boost::detail::variant::void_; T13 = boost::detail::variant::void_; T14 = boost::detail::variant::void_; T15 = boost::detail::variant::void_; T16 = boost::detail::variant::void_; T17 = boost::detail::variant::void_; T18 = boost::detail::variant::void_; T19 = boost::detail::variant::void_]

【问题讨论】:

  • Boost 变体没有虚拟析构函数(或任何其他虚拟函数),表明它不打算用作基类。 IOW,如何实现一个干净的 boost::variant 派生类的简单规则是:“你不能。甚至不要尝试!”
  • @JerryCoffin:我不需要virtual 析构函数,因为我不打算实现任何析构函数(即我不在构造函数中使用new)。尽管如此,我仍然希望有一种方法可以从boost::variant...
  • @olibre:你想通过继承实现什么?
  • @olibre: Jerry 担心的是你可能会写boost::variant&lt;char,bool&gt; *p = new MyVariant(); delete p;。无论MyVariant 的析构函数是什么样的,或者您是否定义了析构函数,这都会有未定义的行为。
  • @Jerry Coffin 没有虚拟析构函数意味着它不打算用作多态基类

标签: c++ gcc boost derived-class boost-variant


【解决方案1】:

我想要一个空状态

boost::variant<boost::blank, bool, long, std::string>

那里。那要容易得多。无需繁琐的继承。

我想给出枚举类型

enum Type
{
  NONE,
  BOOL,
  LONG,
  STRING
};

struct GetType : public boost::static_visitor<Type>
{
  Type operator()(boost::blank) {return NONE;}
  Type operator()(bool) {return BOOL;}
  Type operator()(long) {return LONG;}
  Type operator()(const std::string&) {return STRING;}
};

//Get the type
Type t = boost::apply_visitor(GetType(), theData);

这也很容易。另外,如果您向变体添加新类型,如果您不更新 GetType 以匹配,您的代码将会中断。

其他两个条件要求您使用类,但您不需要继承。你需要收容。

typedef boost::variant<boost::blank, std::string, long> VarType;

class MyVariant
{
public:
    //Default construction will initialize with boost::blank.
    MyVariant(char c) : m_var(std::string(1,c)) {}
    MyVariant(const char* s) : m_var(std::string(s)) {}
    MyVariant(int v) : m_var(long(v)) {}
    MyVariant(long v) : m_var(long(v)) {}

    VarType &operator *() {return m_var;}
    const VarType &operator *() const {return m_var;}

private:
    VarType m_var;
};

...

Type t = boost::apply_visitor(GetType(), *theData);

【讨论】:

  • 将最后一对成员函数称为operator* 肯定比将它们称为get() 更好吗?得墨忒耳法则的人会追捕你并杀死你。
  • @SteveJessop:他想写一个类的唯一目的是在向其中传递值时不必键入std::string(...)long(...)。简洁显然是一个因素。如果是我的代码,我什至不会把它变成一个新类。
  • 谢谢@NicolBolas。你知道我图书馆的用户想写MyVariant v = "string";。我还希望我的库检测用户何时未分配任何值(或未设置 MyVariant)。干杯
  • @NicolBolas:使用访问者进行类型检索是一个非常好的主意,但比基本的variant::which() 绕过消耗更多的资源。即使访问者不太容易出错(类型不一致的编译错误)我仍然会选择Type type() const { return (Type) which(); }。你有什么看法?
【解决方案2】:

最后我的实现基于 Nicol Bolas 的回答:

Variant.h

class Variant 
{
  public:
  Variant()                     : v_(boost::blank()) {}
  Variant(bool     v)           : v_(v)              {}
  Variant(long     v)           : v_(v)              {}
  Variant(int      v)           : v_(long(v))        {}
  Variant(double   v)           : v_(v)              {}
  Variant(const std::string& s) : v_(s)              {}
  Variant(const char*        s) : v_(std::string(s)) {}

  typedef boost::variant <boost::blank, bool, 
                          long, double, std::string> bstvar;
  enum Type {                      
    NONE, BOOL,          // above underlying types
    LONG, DOUBLE, STRING // and enum must be consistent:
  };                     // (order must be same)

  operator          bstvar&  ()       { return v_; }
  operator    const bstvar&  () const { return v_; }
  bstvar      &     operator*()       { return v_; }
  bstvar const&     operator*() const { return v_; }
  bool              empty    () const { return  type() == NONE;          }
  bool              toBool   () const { return  boost::get<bool  > (v_); }
  long              toLong   () const { return  boost::get<long  > (v_); }
  double            toDouble () const { return  boost::get<double> (v_); }
  std::string const& toStr   () const { return  boost::get<std::string> (v_); }
  std::string      & toStr   ()       { return  boost::get<std::string> (v_); }
  inline Type        type    () const { return  (Type) v_.which();       }
  static Type        type    (const std::string&);
  static std::string type    (Type t);

  private:  bstvar v_;  // Data
};

Variant.cpp

#include "Variant.h"
#include <sstream>
#include <algorithm> //std::transform

Variant::Type   Variant::type   (const std::string& str)
{
  std::string lower = str;
  std::transform (lower.begin(), lower.end(), lower.begin(), ::tolower);

  if (lower == "bool")     return BOOL;
  if (lower == "long")     return LONG;
  if (lower == "double")   return DOUBLE;
  if (lower == "string")   return STRING;
  else                     return NONE;
}

std::string  Variant::type  (Type t)
{
  switch (t)
  {
    case NONE:    return "none";
    case BOOL:    return "bool";
    case LONG:    return "long";
    case DOUBLE:  return "double";
    case STRING:  return "string";

    default:
      ;//see below
  }
  std::ostringstream oss;
  oss <<"Unexpected type="<< t;
  return oss.str();
}

【讨论】:

    【解决方案3】:

    接受的答案和其他答案不回答问题。从 boost::variant 派生有充分的理由。

    • 您想在变体中添加方法,使其表现得更加多态
    • 您想继续使用boost::apply_visitor(visitor(), variant) 而不用担心

    这是一个继承自 boost::variant 的类型 MyVariant 的工作示例。代码需要C++11继承boost::variant的(复杂)构造函数。

    编辑:boost::variant 中实现的相等比较等运算符不会自动适用于派生类。这是boost::variant 实现方式的(意外)结果,它明确防止使用模板与“外来”类型进行比较。可以通过显式重载启用运算符,如示例所示。

    #include<iostream>
    #include<boost/variant.hpp>
    
    struct type_size_visitor : public boost::static_visitor<unsigned> {
      template <typename T>
      unsigned operator()(const T&) const { return sizeof(T); }
    };
    
    template <typename... Types>
    class MyVariant : public boost::variant<Types...>
    {
      using base_type = boost::variant<Types...>;
    public:
      // inherit constructors
      using base_type::base_type;
    
      // add a new method
      unsigned type_size() { return boost::apply_visitor(type_size_visitor(), *this); }
    
      /* Inheriting operators from boost::variant does not
         work as it normally would. We have to add explicit
         overloads for the operators we want to use.
      */
      bool operator==(const MyVariant& rhs) const {
        // cast rhs to base and delegate to operator in base
        return base_type::operator==(static_cast<const base_type&>(rhs));
      }
    };
    
    int main() {
      MyVariant<std::string, char> v;
      v = 1;
      std::cout << v.type_size() << " " << sizeof(char) << std::endl;
      v = std::string("foo");
      std::cout << v.type_size() << " " << sizeof(std::string) << std::endl;
    
      // comparison operators need workaround shown above
      MyVariant<std::string, char> a, b;
      a = 1; b = 1;
      assert(a == b);
    }
    

    【讨论】:

    • 不错的答案,是否也可以继承其他语义,例如比较和运算符??
    • @AKludges:我在回复您时扩展了答案。
    • 你好,我尝试了给定的代码,我收到一个错误的clang:error: invalid operands to binary expression ('std::__1::basic_ostream&lt;char&gt;' and 'const ifcinteger') out_ &lt;&lt; operand;
    • @nmd_07 我看起来您缺少 operator
    猜你喜欢
    • 2012-11-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-10
    • 1970-01-01
    相关资源
    最近更新 更多