2

我正在编写一个配置文件解析器,它将配置文件的格式读入key: value定义为std::map<string, ConfigVariantConfigVarianttypedef boost::variant<double, long int, std::string> ConfigVariant;

以下是我的配置类的重要部分:

typedef boost::variant<double, long int, std::string> ConfigVariant;

class Config {
private:
  std::map<std::string, ConfigVariant> config_values;

  // This struct is important
  struct Proxy {
  public:
    template<typename T> T as() { return boost::get<T>(data); }
    template<typename T> operator T() { return as<T>(); }

  private:
    ConfigVariant data;

    Proxy(ConfigVariant data) { this->data = data; }
    Proxy(const Proxy &other);
    Proxy & operator=(const Proxy &other);
    friend class Config;
  };

public:
  Config(std::string config_file_name);
  Proxy operator[] (const std::string &key); // Important
};

唯一相关的实现是这样的operator[]

Config::Proxy Config::operator[] (const string &key) {
  return Proxy(config_values[key]);
}

这段代码允许我有这样的东西:

// config_file
double_key: 4.2

// driver.cpp
int main() {
  Config config("path/to/config_file");

  double d = config["double_key"] // Look ma, no cast
  d += 1

  cout << d << endl; // Prints 5.2

  return 0;
}

我对这种行为感到满意,但如您所见,我无法从operator[]. 因为我没有返回引用,所以我无法通过operator[]. 这有点问题,因为我想在从哈希加载配置值后修改它们。我也想保留config["key"]语法。有任何想法吗?

编辑:

我已将 Proxy 更改为 store ConfigVariant *,然后尝试重载operator=。这正是我尝试过的:

Proxy & operator=(ConfigVariant &other) { *data = other; return *this; }

这给了我以下错误:

app/driver.cpp: In function ‘int main()’:
app/driver.cpp:11: error: no match for ‘operator=’ in ‘Config::operator[](const std::string&)(((const std::string&)(& std::basic_string<char, std::char_traits<char>, std::allocator<char> >(((const char*)"key3"), ((const std::allocator<char>&)((const std::allocator<char>*)(& std::allocator<char>()))))))) = 5.20000000000000017763568394002504646778106689453125e+0’
app/../config/config.hpp:37: note: candidates are: Config::Proxy& Config::Proxy::operator=(const Config::Proxy&)
app/../config/config.hpp:44: note:                 Config::Proxy& Config::Proxy::operator=(ConfigVariant&)

然后我尝试了:

ConfigVariant & operator=(ConfigVariant &other) { *data = other; return *data; }

这给了我几乎相同的错误:

app/driver.cpp: In function ‘int main()’:
app/driver.cpp:11: error: no match for ‘operator=’ in ‘Config::operator[](const std::string&)(((const std::string&)(& std::basic_string<char, std::char_traits<char>, std::allocator<char> >(((const char*)"key3"), ((const std::allocator<char>&)((const std::allocator<char>*)(& std::allocator<char>()))))))) = 5.20000000000000017763568394002504646778106689453125e+0’
app/../config/config.hpp:37: note: candidates are: Config::Proxy& Config::Proxy::operator=(const Config::Proxy&)
app/../config/config.hpp:44: note:                 ConfigVariant& Config::Proxy::operator=(ConfigVariant&)
4

2 回答 2

2

Proxy当前存储地图的副本ConfigVariant。相反,让它在地图内存储指向原始对象的引用或指针。然后您可以实施Proxy::operator=以分配回该值。

于 2013-07-27T04:30:18.150 回答
1

您实际上并没有“推断变体的类型”。您刚刚将变体转换为目标变量的类型是完全隐式的。

我根本不推荐这个,隐式转换已经够糟糕了,不用变体来增加“自动做错事”的风险。

也就是说,由于转换仅在变量包含您要转换为的确切类型的假设下才起作用,因此您可以轻松地返回引用:

typedef boost::variant<double, long int, std::string> ConfigVariant;

class Config {
private:
  std::map<std::string, ConfigVariant> config_values;

  // This struct is important
  struct Proxy {
  public:
    template<typename T> T& as() { return boost::get<T>(data); }
    template<typename T> operator T&() { return as<T>(); }

  private:
    ConfigVariant& data;

    Proxy(ConfigVariant& data) { this->data = data; }
    Proxy(const Proxy &other);
    Proxy & operator=(const Proxy &other);
    friend class Config;
  };

public:
  Config(std::string config_file_name);
  Proxy operator[] (const std::string &key); // Important
};

现在您应该能够:

// driver.cpp
Config config("path/to/config_file");

double& d = config["double_key"] // Look ma, no cast
d += 1;                          // edits the Config instance
于 2013-07-27T12:56:32.820 回答