该问题与以下内容有关:template<class...Types> class variant
:
根据variant.variant/3,没有模板参数实例化variant
的程序格式错误。
到目前为止,如此清晰。现在我有一个关于转换构造函数(template<class T> constexpr variant(T&& t) noexcept(see below)
)的问题:
variant.variant/variant.ctor-16.1说转换的构造函数不得参与重载解析,除非:
sizeof ...(Types)不为零
(...和我暂时不在乎的其他一些要求)。
我的问题是,当没有模板参数的variant
已经使我的程序格式错误时,为什么还要关心转换的构造函数是否参与重载解析?
看看MSVC和variant
的libstdc ++的实现,它们实际上在转换构造函数的声明中有一个enable_if_t<sizeof...(_Types) != 0>
。为什么?
答案 0 :(得分:6)
条款“ sizeof ...(Types)为非零” 已添加到 [variant.ctor]
中,作为论文的一部分:Some improvements to class template argument deduction integration into the standard library也允许variant
支持。
相关摘录:
启用变体支持
以下代码无法编译
variant<int, double> v1(3); variant v2 = v1; // Ill-formed! <--THIS
由于此自然代码很有用,并且其失败令人困惑,因此我们建议对其进行支持。的确,在采用p0510r0禁止
variant<>
之前,上述代码按预期工作,因为variant<>
出现在重载集中的某些推论中。由于不清楚在采用p0510r0时是否考虑过构造函数模板自变量的推导,因此我们想考虑允许variant<>
在这种情况下不产生硬错误。措辞(已添加重点)
如下更改§23.7.3.1p16[variant.ctor]
:
备注:除非sizeof...(Types)
非零,除非is_same_v<decay_t<T>, variant>
为false
,除非decay_t<T>
都不是该函数的专业化,否则此函数不得参与重载解析。in_place_type_t
也不是in_place_index_t
的特殊化,除非is_constructible_v<Tj, T>
是true
,并且除非表达式FUN(std :: forward(t))(其中FUN是上述内容)虚函数集)。
因此,std::variant v2 = v1;
在未考虑添加子句的编译器版本中会失败(例如GCC 7.1。请参见DEMO),但在更高版本中会成功(从GCC 7.2起)。请参见{{3} }。