【问题标题】:C++ operator[] overloading with template accessing boost::variant使用模板访问 boost::variant 的 C++ 运算符 [] 重载
【发布时间】:2014-07-31 14:48:08
【问题描述】:

我有一个带有 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: 注意:模板 T& A::operator[](const string&)

也许我错过了一些微不足道的东西,但我不明白我错在哪里..

【问题讨论】:

  • 我在您的[] 调用中没有看到T,但operator[] 需要T。编译器应该如何推断它?魔法?
  • 那么我应该如何将 ["param"] 替换为...?

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


【解决方案1】:

从这里开始:

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

您将其称为a.get&lt;int&gt;("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 伪引用,你需要另一个类来表示它(没有=operator T const&amp; 而不是operator T&amp;)。

在实践中,这种恶意几乎是不值得的。

我是用 C++11 写的,因为用 C++03 写会稍微痛苦一些(并且遇到pseudo_ref 的终身问题——如果你有auto&amp;&amp; x = a["hello"],它们仍然存在),而且更少痛是好的。

【讨论】:

  • 嗯..看起来很复杂,但可以工作..我现在试试!
  • 嗯.. 在运算符 T&()&&{...} 处给我“'&&' 标记之前的预期初始化程序”
【解决方案2】:
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 正在尝试类似的事情。 我已将来自 map_[key]MultiType&amp; 封装在代理中,然后将工作留在转换(类型转换)运算符上。就是这样。

没有分配的简单a[""] 让您获得代理。 double d = a["double"] 将尝试将 proxy 转换为 double 并因此调用 proxy::operator double&amp;() (我必须对其进行测试,因为我不确定类型推导是否可以正常工作或需要更多工作- 好吧,它有效!)

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

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

附注 #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&lt;std::string, MultiType&gt;(不用class A或任何proxy)。

【讨论】:

  • 嗯.. 然后从 main 我怎么称呼 a[""]?
  • 和你做的一样,或者 double d = a["double"], int i = a["int"];
  • 没有operator= 是否可以使用a["double"] = 3.0
  • 不,它必须添加到代理上。但是如果我们允许operator=,那么为什么要使用代理?为什么不直接返回MultiType&amp;?为什么要定义class A 而不是直接使用地图? ....我假设他想以不同的方式控制它(我们可以修改代理以返回 Tconst T&amp; 以确保我们的 class A 的行为类似于 const class A
  • 我做不到 a["param"] = true;我知道我需要定义 proxy& operator[](const std::string& key);但是我必须返回什么?
【解决方案3】:
template<class T>
T& operator[](const std::string& key) {
    return boost::get<T>(map_[key]);
}

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

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

我怀疑这是你所追求的,但我知道什么。

【讨论】:

  • 但是 "keyTypeToString" 有效......而且政策是一样的......不是吗?
  • 你的代码有效,但如果我只想使用 [""]?
  • 你不能,你需要让编译器知道 T 是什么。
猜你喜欢
  • 1970-01-01
  • 2011-04-06
  • 2023-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多