SFINAE超载,必须考虑哪些规则

Kla*_*aus 1 c++ templates sfinae c++14

我不知道使用SFINAE进行方法重载必须考虑哪些规则.我在问题中多次运行,因为据我所知,接缝更多涉及规则.所以我希望有一套规则可以简单解释,以帮助解决一般问题,而不是一次又一次地提问.

我的起点在这里: 如果需要参数包,请使用SFINAE专门课程

代码1

class AA { public: using TRAIT = int; };
class BB { public: using TRAIT = float; };

template < typename T>
class Y
{   
    public:
        template <typename U = T, typename V= typename std::enable_if< std::is_same< int, typename U::TRAIT>::value, int >::type>
            Y( ) { std::cout << "First" << std::endl; }

        template <typename U = T, typename V= typename std::enable_if< !std::is_same< int, typename U::TRAIT>::value, float >::type>
            Y( ) { std::cout << "Second" << std::endl; }

};
Run Code Online (Sandbox Code Playgroud)

错误:'模板模板Y :: Y()'无法重载

对于这个问题,我得到了一个评论:

""构造函数不能重载"问题可以通过向其中一个构造函数添加虚拟和默认模板参数(如typename Z = void)来解决"

好的,将我的代码更改为:

代码2

template < typename T>
class Y
{
    public:
        template <typename U = T, typename V= typename std::enable_if< std::is_same< int, typename U::TRAIT>::value, int >::type, typename X=int>
            Y( X* = nullptr) { std::cout << "First" << std::endl; }

        template <typename U = T, typename V= typename std::enable_if< !std::is_same< int, typename U::TRAIT>::value, float >::type>
            Y( ) { std::cout << "Second" << std::endl; }
};
Run Code Online (Sandbox Code Playgroud)

好吧,它编译.但简化接缝工作也让我怀疑.

代码3

template < typename T>
class Y
{   
    public:
        template <typename U = T, typename V= typename std::enable_if< std::is_same< int, typename U::TRAIT>::value, int >::type>
            Y( int* = nullptr) { std::cout << "First" << std::endl; }

        template <typename U = T, typename V= typename std::enable_if< !std::is_same< int, typename U::TRAIT>::value, float >::type>
            Y( ) { std::cout << "Second" << std::endl; }
};
Run Code Online (Sandbox Code Playgroud)

最后一个例子也有效!它在一个构造函数中有一个默认参数,但该参数不是模板化参数.

对我来说,现在还不清楚哪些规则正在努力让事情在这里运行.

我的第一个误解是,我认为SFINAE是通过实例化构造函数模板而发生的,只有一个构造函数可用.这不是事实!所有构造函数的每个参数集必须不同!?为什么?另外,为什么它应该或必须是模板参数?我的例子3接缝工作,但其他人给了我建议使用dummy and defaulted template parameter (like , typename Z = void).有人可以给我一些关于这个主题的背景信息吗?

Jar*_*d42 5

函数的签名应该不同,默认值(对于常规参数的模板)不是签名的一部分.所以

template <typename U = T, typename V= typename std::enable_if< std::is_same< int, typename U::TRAIT>::value, int >::type>
Y();

template <typename U = T, typename V= typename std::enable_if< !std::is_same< int, typename U::TRAIT>::value, float >::type>
Y();
Run Code Online (Sandbox Code Playgroud)

很简单

template <typename U, typename V>
Y();

template <typename U, typename V>
Y();
Run Code Online (Sandbox Code Playgroud)

所以,重新定义.

template <typename U = T, typename V = typename std::enable_if< std::is_same< int, typename U::TRAIT>::value, int >::type, typename X=int>
Y( X* = nullptr);

template <typename U = T, typename V= typename std::enable_if< !std::is_same< int, typename U::TRAIT>::value, float >::type>
Y( );
Run Code Online (Sandbox Code Playgroud)

template <typename U, typename V, typename X>
Y(X*);

template <typename U, typename V>
Y();
Run Code Online (Sandbox Code Playgroud)

所以不同的签名.

避免总是添加参数的方法是做类似的事情

template <typename U = T, typename std::enable_if<std::is_same<int, typename U::TRAIT>::value>::type* = nullptr>
Y();

template <typename U = T, typename std::enable_if<!std::is_same<int, typename U::TRAIT>::value>::type* = nullptr>
Y();
Run Code Online (Sandbox Code Playgroud)

导致不同的签名:

template <typename U, typename std::enable_if<std::is_same<int, typename U::TRAIT>::value>::type*>
Y();

template <typename U, typename std::enable_if<!std::is_same<int, typename U::TRAIT>::value>::type*>
Y();
Run Code Online (Sandbox Code Playgroud)