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

Cur*_*ous 11 c++ variant c++17

为什么构造函数(4)std::variant来自http://en.cppreference.com/w/cpp/utility/variant/variant?似乎它会在代码中引起很多歧义,否则可以通过显式来避免.例如,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
Run Code Online (Sandbox Code Playgroud)

有些情况下绝对需要吗?

另一个问题是为什么需要从同一个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)
Run Code Online (Sandbox Code Playgroud)

Nic*_*las 10

有些情况下绝对需要吗?

不会.但事情并没有增加,因为它们"绝对需要".它们被添加,因为它们很有用.

并且可以从其中一种组件类型中隐式转换对于a非常有用variant.是的,它会在某些极端情况下产生歧义.但这种模糊性通常是由于类型设计中的缺陷(比如字符串文字更喜欢转换为bool超过用户定义的转换).

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

(5)和(7)不符合(6)和(8)的目的吗?

不合理的方式.

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

using type = vector<int>;
variant<type> t(in_place<type>, 6);
Run Code Online (Sandbox Code Playgroud)

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

现在,您可以这样做:

variant<type> t(in_place<type>, initializer_list<int>{6});
Run Code Online (Sandbox Code Playgroud)

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

variant<type> t(in_place<type>, {6});
Run Code Online (Sandbox Code Playgroud)

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

除了其他方式之外,模板推导与推导不同,auto因为它不会initializer_listbraced-init-list表达式中推导出来.例如

template <typename Type>
void func(const Type&);
Run Code Online (Sandbox Code Playgroud)

不会演绎Type成为一个std::initializer_list下列电话

func({1, 2, 3, 4, 5});
Run Code Online (Sandbox Code Playgroud)

有关此内容的更多信息,请参阅通用引用和std :: initializer_list.

  • @Curious实际上我意识到这一定是以前一直要求的; http://stackoverflow.com/a/17622767/481267 (3认同)