我可以使用braced-init-list作为std :: variant的向量吗?

时间:2018-02-15 17:28:51

标签: c++ initialization variant c++17

我有2个包含不同数据的结构,每个结构都有一个方法将这些数据序列化为JSON字符串:

struct Struct1
{
  Struct1(int value) : value(value){};

  int value;

  std::string ToJSON() const
  {
    std::string ret = "{value: " + std::to_string(value) + "}";
    return ret;
  }
};

struct Struct2
{
  Struct2(std::string id, std::string image, std::string name) : id(id), image(image), name(name){};

  std::string id;
  std::string image;
  std::string name;

  std::string ToJSON() const
  {
    return "{id: " + id + " image: " + image + " name: " + name + "}";
  }
};

我需要将其中的几个存储在一个容器中,以便稍后迭代它们并从每个对象获取JSON字符串。我使用std::variant执行此操作。

std::vector<std::variant<Struct1, Struct2>> v3;

然后我可以像这样迭代容器:

auto GetJSONString = [](auto&& _in){return _in.ToJSON();};

for (const auto& nextV : v3)
{
  auto test = std::visit(GetJSONString, nextV);
  std::cout << test << " ";
}
std::cout << std::endl;

在我尝试使用braced-init-lists填充向量之前,一切正常。

换句话说,这是有效的:

std::vector<std::variant<Struct1, Struct2>> v{Struct1(5), Struct2("someid", "someimage", "somename")};

但这不是:

std::vector<std::variant<Struct1, Struct2>> v4{ {13}, {"someid", "someimage", "somename"}};

在不工作的代码上,我得到以下编译器错误:

error: no matching function for call to 'std::vector<std::variant<Struct2, Struct1> >::vector(<brace-enclosed initializer list>)

我不明白为什么会这样。在这种情况下我不能使用大括号初始化吗?如果是的话,......为什么?或者我是否需要以某种方式修改我的结构以支持这种初始化?

Here是wandbox.org上的一个最小工作示例,用于进一步说明我的问题。

2 个答案:

答案 0 :(得分:2)

  

在这种情况下我不能使用大括号初始化吗?如果是的话,......为什么?或者我是否需要以某种方式修改我的结构以支持这种初始化?

不,你不能。不,没有任何改变可以让它发挥作用。这是一个非常简单的例子:

template <typename T> void foo(T&& );
foo({1}); // what is T?

Braced-init-lists没有类型,因此无法推断出它们。 variant构造函数的工作方式是推导其参数,然后选择从中初始化的最佳替代方法。它不能从你的braced-init-list那样做,你必须使用一个有类型的表达式。

唯一可行的方法是variant<A, B, C>本身有非模板构造函数variant(A&& )variant(B&& )variant(C&& )然后,braced-init-lists会起作用。

答案 1 :(得分:1)

您打算让代码调用的variant constructor

template<typename T>
constexpr variant(T&& t)

但该构造函数模板的模板参数推断将失败,因为braced-init-lists do not have a type

使用以std::in_place_type_t<T>作为第一个参数的构造函数可以更接近

std::vector<std::variant<Struct2, Struct1>> v4{
     { std::in_place_type<Struct1>, 13 },
     { std::in_place_type<Struct2>, "someid", "someimage", "somename" }
};

但这也失败了,因为有问题的variant构造函数是explicitill-formed for copy-list-initialization

因此,您列出的选项是最佳选择

std::vector<std::variant<Struct2, Struct1>> v4{
     Struct1{ 13 },
     Struct2{ "someid", "someimage", "somename" }
};