我可以避免为std :: variant中的每个结构显式编写构造函数吗?

时间:2017-10-03 08:41:58

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

考虑以下代码:

#include <variant>

struct x {
  int y;
};

int main() {
  std::variant<x> v(std::in_place_type<x>, {3}); /*1*/
  return std::get<x>(v).y;
}

这不会编译,也不会从行{}中删除/*1*/,即使是聚合初始化

x a{3};
x b({3});

以“类似构造函数”的形式工作。我可以以某种方式让std::variant初始化器知道使用聚合初始化构造结构的可能性,而不必为我的实际案例中可能使用的每个结构编写无聊的样板构造函数吗?

我希望这可行,不知何故,根据cppreference,两个重载(5)和(6)都说

  

使用参数 [...]

构造带有指定替代T的变体和 初始化 包含的值

如果重要的话,我正在使用GCC 7.

3 个答案:

答案 0 :(得分:3)

也许这不是你所要求的,但是明确构建对象而不依赖于类型推断呢?

#include <variant>

struct x {
  int y;
};

int main() {
  std::variant<x> v(std::in_place_type<x>, x{3});
  return std::get<x>(v).y;
}

答案 1 :(得分:1)

除了添加构造函数之外,没有解决方法。标准规定了你提到的重载,分别是[variant.ctor]19[variant.ctor]23

  

效果:初始化包含的值,就像使用参数T直接非列表初始化std​::​forward<Args>(args)...类型的对象一样。

     

效果:初始化包含的值,就像使用参数T直接非列表初始化il, std​::​forward<Args>(args)...类型的对象一样。

您始终可以使用以下方式复制或移动对象:

std::variant<x> v(std::in_place_type<x>, x{3});
// or more clear and does the same thing
std::variant<x> v(x{3});

答案 2 :(得分:0)

如果您想要过度杀戮,我们可以创建一个具有转换运算符的工厂类型:

template <class... Args>
struct list_init_from {
    std::tuple<Args...> args;

    template <class T>
    operator T() {
        return std::apply([](auto... args){
            return T{args...};
        }, args);
    }   
};

template <class... Args>
list_init_from(Args... ) -> list_init_from<Args...>;

您可以使用:

std::variant<x> v(std::in_place_type<x>, list_init_from{3});

这很有用,但还有很多不足之处:完美转发,转换操作符上的SFINAE,以及明确指定允许转换的类型是留给读者的练习。