C ++ operator []重载模板访问boost :: variant

时间:2014-07-31 14:48:09

标签: c++ templates boost operator-overloading c++03

我的这个类有一个map属性,其值是boost :: variant。

typedef boost::variant<char, int, bool, unsigned short, float, timeval, double > MultiType;

class A {
public: 
    template<class T>
    T& operator[](const std::string& key) {
        return boost::get<T>(map_[key]);
    }
    template<class T>
    std::string keyTypeToString(const std::string& key) {
        std::stringstream ss;
        ss << boost::get<T>(map_[key]);
        return ss.str();
    }
private:
    std::map<std::string, MultiType> map_; 
};

来自main:

A a;
a["param"];

编译器报告此错误:

  

../ src / main.cpp:8:25:错误:'a [“param”]'中'运算符[]'不匹配   ../src/main.cpp:8:25:注意:候选人是:
  ../src/util/A.h:53:5:注意:模板T&amp; A :: operator [](const string&amp;)

也许我错过了一些微不足道的事情,但我无法理解我错在哪里......

3 个答案:

答案 0 :(得分:4)

从这开始:

template<class T>
T& get(const std::string& key) {
    return boost::get<T>(map_[key]);
}

您可以将其称为a.get<int>("hello"),其中元素"hello"int

接下来,写下这个:

struct pseudo_ref {
  std::string const& str;
  A* a;
  template<typename T>
  operator T&()&&{
    return a->get<T>(str);
  }
  template<typename T>
  pseudo_ref operator=( T&& t ) && {
    a->get<typename std::decay<T>::type>(str) = std::forward<T>(t);
    return {str, a};
  }
  pseudo_ref(pseudo_ref const&)=delete;
  pseudo_ref& operator=(pseudo_ref const&)=delete;
  pseudo_ref( std::string const& s, A* a_ ):str(s), a(a_) {}
};

然后回到A

pseudo_ref operator[](std::string const& str) {
  return {str, this};
}

我们得到[]神奇地为你转换,只要你使用完全正确的类型分配/读取它。

这有点危险,但很酷。

如果您想要const pseudo_ref,则需要另一个类来表示它(没有=operator T const&而不是operator T&。)

在实践中,这种malarkey很少值得。

我在C ++ 11中写过这个,因为在C ++ 03中编写它会稍微有点痛苦(并且遇到pseudo_ref的终生问题 - 如果你有auto&& x = a["hello"]它们仍然存在),疼痛少就好了。

答案 1 :(得分:3)

template<class T>
T& operator[](const std::string& key) {
    return boost::get<T>(map_[key]);
}

编译器无法从T之类的调用中推断出a["param"];。您需要明确指定

a.operator[]<int>("param");

我怀疑你是在追求什么,但我知道什么。

答案 2 :(得分:3)

class A {
public:
    class proxy {
        friend class A;
    private:
        MultiType& it;
        proxy(MultiType& it): it(it) {}
    public:
        template<typename T>
          operator T&() {
            return boost::get<T>(it);
        }
    };
    proxy operator[](const std::string& key) {
        return proxy(map_[key]);
    }
private:
    std::map<std::string, MultiType> map_; 
};

<强>说明

我可以看到Yakk正在尝试类似的事情。我已经在代理中封装了来自MultiType&的{​​{1}},然后将工作留给了转换(type-cast)运营商。就是这样。

没有分配的简单map_[key]可以获得代理。 a[""]会尝试将double d = a["double"]转换为proxy,从而调用double (我必须对其进行测试,因为我不确定类型扣除是否会起作用它是或将需要更多的工作 - 嗯,它的工作原理!)

AFTERNOTE:从问题和代码中不清楚提供了哪些操作是允许的。我们可以通过更改类型转换运算符的签名来修改proxy::operator double&()以允许其他操作或更多proxy,而不是返回readonly

允许修改会引发问题:为什么不直接使用const T& ? (从MultiType&返回)这导致了一个问题:为什么A::operator[]

AFTERNOTE#2: boost::variant没有类型转换操作符,并且必须有它的原因。想想这段代码:

class A

运行时异常!我认为最好的解决方案是将MultiType子类化并在那里定义类型转换运算符(同时检查boost::variant::which())。

指派现有名人

int i = a["double"]

...但上述内容只有在我们已经在地图中有一些价值时才有效。

class A { ...
    class proxy { ...
        template<class T> proxy& operator=(const T& rhs) {
            it = rhs; return *this; }

COMPLETE REDISIGN:

class A { ...
    A() { map_["pi"] = 3.14; } ...
a["pi"] = 3.1415;

现在我们可以直接使用class MultiType: public boost::variant<int, double, ...> { public: template<class T> operator T() { switch(which()) { case 0: return boost::get<int>(*this); case 1: return boost::get<double>(*this); ... (不使用std::map<std::string, MultiType>或任何class A)。