C++运算符[]重载,模板访问boost::variant

C++ operator[] overloading with template accessing boost::variant

本文关键字:访问 boost variant 运算符 重载 C++      更新时间:2023-10-16

我有一个带有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_; 
};

来自主:

A a;
a["param"];

编译器报告此错误:

/src/main.cpp:8:25:错误:'a["param"]'中的'operator[]'不匹配
../src/main.cpp:8:25:注意:候选者是:
../src/util/A.h:53:5:note:template T&A: :运算符[](常量字符串&(

也许我错过了一些琐碎的事情,但我不明白我错在哪里了。。

从这个开始:

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&(。

在实践中,这种胡言乱语很少值得

我在C++11中写了这篇文章,因为在C++03中写这篇文章会稍微痛苦一些(并且会遇到pseudo_ref的终身问题——如果你有auto&& x = a["hello"],这些问题仍然存在(,减少痛苦是好事。

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_; 
};

解释:

我看得出来亚克也在尝试类似的事情我已经在代理中封装了map_[key]中的MultiType&,然后留下了转换(类型转换(运算符的工作。仅此而已。

无需分配的简单a[""]将为您提供代理。double d = a["double"]将尝试将proxy转换为double,从而调用proxy::operator double&()(我不得不测试它,因为我不确定类型推导是否能正常工作,或者需要更多的工作-好吧,它能工作!(

下午注意:从提供的问题和代码中不清楚允许哪些操作。我们可以修改proxy以允许其他操作,或者通过更改类型转换运算符的签名来返回const T&,使其更像readonly

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

AFTERNOTE#2:boost::variant没有类型转换运算符,这一定是有原因的

int i = a["double"]

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

分配给现有名称:

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

但只有当我们在地图中已经有了一些价值时,以上内容才能发挥作用。

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或任何proxy(。

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

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

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

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