关于std :: variant的三个构造函数的问题提出了接口

时间:2016-08-30 21:55:35

标签: c++ variant c++17

为什么std::variant的{​​{1}}存在构造函数(4)?似乎它会在代码中引起很多歧义,否则可以通过显式来避免。例如,cppreference上的代码示例突出显示用户可能不会注意到的可能的歧义(第三行)

variant<string> v("abc"); // OK
variant<string, string> w("abc"); // ill-formed, can't select the alternative to convert to
variant<string, bool> w("abc"); // OK, but chooses bool

是否存在绝对需要的情况?

另一个问题是为什么需要在同一个cppreference页面中使用构造函数(6)和(8)。赢得(5)和(7)是否符合(6)和(8)的目的?我可能误解了他们的用法..

对于读者来说,我在问题中提到的构造函数是

constexpr variant();              // (1)    (since C++17)

variant(const variant& other);    // (2)    (since C++17)

variant(variant&& other);         // (3)    (since C++17)

template< class T >               // (4)    (since C++17)
constexpr variant(T&& t);

template< class T, class... Args >
constexpr explicit variant(std::in_place_type_t<T>, Args&&... args); // (5) (since C++17)

template< class T, class U, class... Args >
constexpr explicit variant(std::in_place_type_t<T>,
                           std::initializer_list<U> il, Args&&... args); // (6) (since C++17)

template< std::size_t I, class... Args >
constexpr explicit variant(std::in_place_index_t<I>, Args&&... args) // (7) (since C++17)

template <size_t I, class U, class... Args>
constexpr explicit variant(std::in_place_index_t<I>,
                           std::initializer_list<U> il, Args&&... args); // (8) (since C++17)

1 个答案:

答案 0 :(得分:10)

  

是否存在绝对需要的情况?

没有。但事情并没有被添加,因为它们是绝对需要的&#34;。它们被添加,因为它们有用。

从一个组件类型中隐式转换对于variant非常有用。是的,它会在某些极端情况下产生歧义。但这种歧义通常是由于类型设计中的缺陷(比如字符串文字更倾向于通过用户定义的转换转换为bool)。

如果存在含糊不清的情况,那么您只需明确一下即可。就像使用"abc"s UDL文字而不是使用裸字符串文字(这是另一个原因)。但是当你处理精心设计的类型时,没有理由强迫每个人都明确。

  

赢得(5)和(7)是否符合(6)和(8)的目的?

不合理。

在标准的每种情况下,当函数接受将传递给构造函数的可变参数时,它们将在该对象上使用构造函数语法而不是{}语法。所以如果你有这个:

using type = vector<int>;
variant<type> t(in_place<type>, 6);

您将接到vector<int>(6)的电话。请注意,这与vector<int>{6}不同。也就是说,除非您实际传递初始化列表,否则您不会获得初始化列表构造函数。

现在,您可以这样做:

variant<type> t(in_place<type>, initializer_list<int>{6});

但那过于冗长。相比之下:

variant<type> t(in_place<type>, {6});

这远不那么冗长。编译器可以推导出初始化列表的类型。如果您尝试将braced-init-list推断为任意T,则模板参数类型推导会失败。

除其他方式外,模板扣除与auto的扣除不同,因为它不会从initializer_list表达式中推断braced-init-list。例如

template <typename Type>
void func(const Type&);
对于以下调用,

不会将Type推断为std::initializer_list

func({1, 2, 3, 4, 5});

有关此内容的详情,请参阅Universal references and std::initializer_list