MSVC std :: pair实现:SFINAE在这里正确应用了吗?

时间:2018-01-12 20:32:09

标签: c++ templates stl

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>

所以我想知道第一个实现是否正确,如果它是正确的,那么将它改为第二个是什么。

3 个答案:

答案 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个参数,因此重载解析不是问题。