从 Microsoft Visual Studio 15.4.5 附带的 STL 实现中考虑以下std::pair
默认构造函数的代码:
template<class _Uty1 = _Ty1,
class _Uty2 = _Ty2,
class = enable_if_t<is_default_constructible<_Uty1>::value
&& is_default_constructible<_Uty2>::value>>
constexpr pair()
: first(), second()
{ // default construct
}
我设置了/std:c++latest
选项,所以,根据标准(我在这里使用草案 n4659 ),我希望如果{{1},这个构造函数将被排除在重载决策之外或_Ty1
不是默认可构造的:
23.4.2类模板对[pairs.pair]
EXPLICIT constexpr pair();
效果:值 - 初始化第一个和第二个。
备注:此构造函数不应参与重载解析 除非
_Ty1
为真is_default_constructible_v<first_type>
是真的。 [注意:这种行为 可以使用默认模板的构造函数模板实现 参数。]
在上面的实现中,排除按如下方式执行:
is_default_constructible_v<second_type>
据我所知, SFINAE does not work表示模板类型参数的默认值。
有趣的是,在 Microsoft Visual Studio 15.5.3 中,构造函数已更改为“正确版本”(基于我有限的模板知识,“正确”):
class = enable_if_t<is_default_constructible<_Uty1>::value
&& is_default_constructible<_Uty2>::value>
所以我想知道第一个实现是否正确,如果它是正确的,那么将它改为第二个是什么。
答案 0 :(得分:7)
并不是SFINAE在默认模板参数中不起作用;它们不会被视为签名的一部分,因此将SFINAE机器放在那里意味着如果要构建过载集,必须以其他方式使签名不同。
因此,这很好:
template<class T, class=std::enable_if_t<std::is_integral_v<T>>>
T meow();
template<class T, class=std::enable_if_t<!std::is_integral_v<T>>>
void meow();
因为签名不同(返回类型是函数模板签名的一部分 - 但不是函数);这是这样的:
template<class T, class=std::enable_if_t<std::is_integral_v<T>>>
void meow(T);
template<class T, class=std::enable_if_t<!std::is_integral_v<T>>>
void meow(const T&);
但这不是(它重新声明了相同的函数模板,因此尝试给同一模板参数提供两次默认模板参数):
template<class T, class=std::enable_if_t<std::is_integral_v<T>>>
void meow(const T&);
template<class T, class=std::enable_if_t<!std::is_integral_v<T>>>
void meow(const T&);
特别是关于pair
构造函数模板,如果不知道其他构造函数模板是什么,就无法确定它是否正确。也就是说,如果他们弄错了,我会非常惊讶;任何问题都应该通过简单的单元测试轻松捕获。
答案 1 :(得分:0)
我认为他们都是正确的,但第二更灵活。
答案 2 :(得分:0)
我认为这是正确的,因为SFINAE用法的目的是确保构造函数仅在元素是可默认构造时才可用,并且不允许选择所述构造函数的不同重载。
根据定义,只有一个版本的构造函数(或任何其他函数)接受0个参数,因此重载解析不是问题。