【问题标题】:Boost Variant: how to get currently held type?Boost Variant:如何获取当前持有的类型?
【发布时间】:2012-01-10 18:13:36
【问题描述】:

据我了解,boost.variant 的所有类型都被解析为真实类型(意思是好像 boost variant<int, string> a; a="bla-bla" 在编译后会变成 string a; a="bla-bla")所以我想知道:如何让什么类型被放入 boost 变体中?

我尝试了什么:

#include <boost/variant.hpp>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>
#include <iostream>

int main()
{
    typedef boost::function<double (double x)> func0;
    typedef boost::function<double (double x, double y)> func1;
    typedef boost::variant<int, func0, func1> variant_func;
    func1 fn = std::plus<double>();
    variant_func v(fn);
    std::cout << boost::get<func1>(v)(1.0, 1.0) << std::endl; // this works
    //std::cout << boost::get<v::type>(v)(1.0, 1.0) << std::endl; // this does not compile with many errors
    // std::cout << (v)(1.0, 1.0) << std::endl; // this fails with Error    1   error C2064: term does not evaluate to a function taking 2 arguments

    std::cin.get();
    return 0;
}

【问题讨论】:

    标签: c++ boost boost-variant


    【解决方案1】:

    v.which() 将返回当前持有的对象类型的从 0 开始的索引。

    当您检索对象时,您的代码必须使用静态类型(为了满足get&lt;T&gt; 函数模板)来引用(有效)动态类型的对象。

    您需要测试类型(使用which()type())并相应地分支或使用静态访问者。无论选择哪种方式,都必须明确声明要检索的静态类型,并且必须与动态类型匹配,否则将引发异常。

    解决此问题的一种方法是,不要直接使用变体类型,而是使用内部包含变体类型的类,然后定义任何隐式转换运算符,以便以最小的麻烦使用该对象。

    我有一个名为Dynamic C++ 的项目使用了这种技术。

    【讨论】:

    • 它能帮助 boost::get(v)(1.0, 1.0) 或 (v)(1.0, 1.0) 工作吗?
    • 没有。您需要使用 get() 语法或静态访问者来检索变体的内容。
    • Dynamic C++ 是否支持任何类型(例如 int(*)(std::string, int) 类型)?
    • 不,它有一组固定的类型。但是添加新类型非常简单,虽然乏味。特别是如果你想允许类型之间的操作。
    • +1 用于动态 C++,这不是我的事,但我看了一下,它看起来很干净。干得好!
    【解决方案2】:

    boost.variant 有一个 .type() function 可以返回活动类型的 typeid,前提是您已启用 RTTI。

    您还可以定义一个静态访问者来根据变体的内容类型执行操作,例如

    struct SomeVisitor : public boost::static_visitor<double>
    {
        double operator()(const func0& f0) const { return f0(1.0); }
        double operator()(const func1& f1) const { return f1(1.0, 1.0); }
        double operator()(int integer) const { return integer; }
    };
    ...
    std::cout << boost::apply_visitor(SomeVisitor(), v) << std::endl;
    

    【讨论】:

    • 所以如果 RTTI 开启,应该可以调用 `boost::get(v)(1.0, 1.0)`?
    • @myWallJSON:不,因为模板参数必须是编译时已知的类型,而不是运行时 typeid 对象。
    • 所以可能是像((v.type()) v)(1.0, 1.0)这样的任何类型的转换?
    • @myWallJSON:最好使用 static_visitor 访问存储的值,如上所示。如果你使用get,那么就意味着你静态知道当前存储的是哪种类型,或者你尝试将值作为类型获取,然后处理NULL指针或异常。 - RTTI 结果不能用于任何编译时的事情(例如强制转换)。 typeid 是一个包含类型的运行时描述的对象,它不会产生可以在编译时使用的类型名。
    • @myWallJSON: 你可以做boost::get&lt;X&gt;(variant)(1.0, 1.0);,但你最好把它包装在一个try块中,因为如果variant没有存储X类型的对象,就会抛出一个异常。
    【解决方案3】:

    您可以使用以下都导致 std::type_info 对象:

    • boost::variant 的 type() 成员函数,
    • 可应用于任何类型或类型化表达式的 C++ 运算符 typeid(),

    与成员函数 std::type_info::operator== 一起检查 boost::variant 当前存储的类型。例如,

    boost::variant<int, bool, std::string> container;
    container = "Hello world";
    
    if (container.type() == typeid(std::string)) {
        std::cout << "Found a string: " << boost::get<std::string>(container);
    }
    else if (container.type() == typeid(int)) {
        std::cout << "Found an int: " << boost::get<int>(container);
    }
    

    【讨论】:

    • 不!如果变体中包含矢量,这将崩溃并烧毁。即变体, int >>。正如 Ferruccio 所说,使用 which() 代替。
    • 我在 MSVS 2013 上尝试使用 boost::variant&lt;vector&lt;int&gt;, int&gt; 并且成功了。你用什么编译器试过?你能解释一下你认为它在某些情况下不应该起作用的原因吗?
    【解决方案4】:

    您可以使用boost::get 的指针版本。 tutorial 有这个例子:

    void times_two( boost::variant< int, std::string > & operand )
    {
        if ( int* pi = boost::get<int>( &operand ) )
            *pi *= 2;
        else if ( std::string* pstr = boost::get<std::string>( &operand ) )
            *pstr += *pstr;
    }
    

    因此,您可以像通常使用 boost::get 一样使用它,但将指针传递给变体,如果这不是当前存储在变体中的类型,则结果是 nullptr 的指针。如果该类型在变体中的类型列表中出现多次,这将没有用处,但这并不常见。

    【讨论】:

      猜你喜欢
      • 2019-05-10
      • 1970-01-01
      • 1970-01-01
      • 2011-10-26
      • 2013-08-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多