关于 c :boost::any 替换下面的代码

boost::any replacement for the code below

我希望摆脱对我的代码的 boost 依赖。我有以下结构构造。在代码中的另一个位置调用和使用此结构时,使用 boost::any_cast。我知道模板类会做到这一点,但很难编写这个模板。 - C 菜鸟。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 struct Properties {
 public:
 Properties() {}
 Properties(const std::string &s, const boost::any & p) {
      name = s;
      value = p;
 }

 template <typename T>
 Properties(T n) {
      value = n;
 }
 boost::any value;

 std::string name;
};


只是为了好玩,我想我会创建一个极简的任何实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//////////////////////////////////////////
// my_any.hpp
#include <memory>
#include <stdexcept>

struct my_any
{
    my_any() = default;
    template <typename T> my_any(T const& v) : _storage(new storage< T >(v)) { }
    my_any(my_any const& other)              : _storage(other._storage? std::move(other._storage->clone()) : nullptr) {}

    void swap(my_any& other)               { _storage.swap(other._storage); }
    friend void swap(my_any& a, my_any& b) { a.swap(b); };
    my_any& operator=(my_any other)        { swap(other); return *this; }

    // todo move semantics
private:
    struct storage_base {
        virtual std::unique_ptr<storage_base> clone() = 0;
        virtual ~storage_base() = default;
    };
    template <typename T>
    struct storage : storage_base {
        T value;
        explicit storage(T const& v) : value(v) {}
        std::unique_ptr<storage_base> clone() { return std::unique_ptr<storage_base>(new storage< T >(value)); }
    };
    std::unique_ptr<storage_base> _storage;
    template<typename T> friend T      & any_cast(my_any      &);
    template<typename T> friend T const& any_cast(my_any const&);
};

template <typename T> T& any_cast(my_any& a) {
    if (auto p = dynamic_cast<my_any::storage< T >*>(a._storage.get()))
        return p->value;
    else
        throw std::bad_cast();
}

template <typename T> T const& any_cast(my_any const& a) {
    if (auto p = dynamic_cast<my_any::storage< T > const*>(a._storage.get()))
        return p->value;
    else
        throw std::bad_cast();
}

然后您可以按照您的用例所示的方式使用它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
struct Properties {
    public:
        Properties(const std::string &s="", const my_any& p={})
            : name(s), value(p) {}

        template <typename T> Properties(T n) { value = n; }

        std::string name;
        my_any value;
};

#include <vector>
#include <iostream>

typedef std::vector<Properties> Props;

int main()
{
    Props v;
    v.emplace_back("bye", 42);
    v.emplace_back("vector", v);

    std::cout <<"v.size():"          << v.size()                           <<"\
"
;
    std::cout <<"v[0].value:"        << any_cast<int>(v[0].value)          <<"\
"
;
    std::cout <<"v[1].value.size():" << any_cast<Props>(v[1].value).size() <<"\
"
;

    v[0].value = v;

    try {
        std::cout <<"v[0].value:" << any_cast<int>(v[0].value) <<"\
"
;
    } catch(std::exception const& e)
    {
        std::cout << e.what() <<" exception caught, ok!\
"
;
    }

    std::cout <<"v[0].value.size():" << any_cast<Props>(v[0].value).size() <<"\
"
;
}

查看输出 Live On Coliru


boost::any 使用类型擦除来存储任何类型的对象,您可以在运行时为其分配不同类型的值。 any_cast 用于检索存储在 any 对象中的具有正确类型的原始值。例如,您当前的课程允许您这样做

1
2
3
4
5
6
Properties p("int", 42);
std::cout << boost::any_cast<int>(p.value) << '\
'
;
p = Properties("string", std::string("hello"));
std::cout << boost::any_cast<std::string>(p.value) << '\
'
;

您不能只是将上面的类转换为模板并获得相同的功能。如果这样做,您将只能存储一种类型的值。而且您必须将整个 struct 更改为模板,而不仅仅是构造函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template<typename T>
struct Properties {
 public:
 Properties() {}
 Properties(std::string s, T p)
 : name(std::move(s))   // should use initialization list instead
 , value(std::move(p))  // of assignment within the body
 {}

 Properties(T n)
 : value(std::move(n))
 {}

 std::string name;
 T value;
};

但是,我上面发布的代码现在是非法的。

1
2
3
4
5
6
Properties<int> p("int", 42);
std::cout << p.value << '\
'
;
// p = Properties<std::string>("string", std::string("hello"));
// will not compile because Properties<int> and Properties<std::string> are
// distinct types

如果这些限制没问题,那么修改后的定义应该适合你。