关于 std::variant 提议的接口中三个构造函数的问题
Questions about three constructors in std::variant's proposed interface
为什么 http://en.cppreference.com/w/cpp/utility/variant/variant 的 std::variant
存在构造函数 (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) 的目的吗?我可能误解了他们的用法..
对于reader,我在问题中提到的构造函数是
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)
Is there some case where it is absolutely going to be needed?
没有。但是不会添加东西,因为它们是 "absolutely going to be needed"。添加它们是因为它们 有用。
并且可以从其组件类型之一隐式转换对于 variant
非常有用。是的,它在某些极端情况下会产生歧义。但这种歧义通常是由于类型设计的缺陷(比如字符串文字更喜欢转换为 bool
而不是用户定义的转换)。
如果有模棱两可的情况,那么您只需明确说明即可。就像使用 "abc"s
UDL 文字而不是裸字符串文字(这样做的另一个原因)。但是当你处理设计良好的类型时,没有理由强制每个人都明确。
Won't (5) and (7) serve the purposes that (6) and (8) are meant for?
不合理。
在标准中的每一种情况下,当函数接受将传递给构造函数的可变参数时,它们将使用 构造函数语法 而不是 {}
语法那个对象。所以如果你有这个:
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
推导的不同之处在于,它不会从 braced-init-list
表达式推导 initializer_list
s。例如
template <typename Type>
void func(const Type&);
不会将 Type
推断为以下调用的 std::initializer_list
func({1, 2, 3, 4, 5});
有关此内容的更多信息,请参阅 Universal references and std::initializer_list。
为什么 http://en.cppreference.com/w/cpp/utility/variant/variant 的 std::variant
存在构造函数 (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) 的目的吗?我可能误解了他们的用法..
对于reader,我在问题中提到的构造函数是
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)
Is there some case where it is absolutely going to be needed?
没有。但是不会添加东西,因为它们是 "absolutely going to be needed"。添加它们是因为它们 有用。
并且可以从其组件类型之一隐式转换对于 variant
非常有用。是的,它在某些极端情况下会产生歧义。但这种歧义通常是由于类型设计的缺陷(比如字符串文字更喜欢转换为 bool
而不是用户定义的转换)。
如果有模棱两可的情况,那么您只需明确说明即可。就像使用 "abc"s
UDL 文字而不是裸字符串文字(这样做的另一个原因)。但是当你处理设计良好的类型时,没有理由强制每个人都明确。
Won't (5) and (7) serve the purposes that (6) and (8) are meant for?
不合理。
在标准中的每一种情况下,当函数接受将传递给构造函数的可变参数时,它们将使用 构造函数语法 而不是 {}
语法那个对象。所以如果你有这个:
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
推导的不同之处在于,它不会从 braced-init-list
表达式推导 initializer_list
s。例如
template <typename Type>
void func(const Type&);
不会将 Type
推断为以下调用的 std::initializer_list
func({1, 2, 3, 4, 5});
有关此内容的更多信息,请参阅 Universal references and std::initializer_list。