Why SFINAE (enable_if) works from a definition inside a class, but not externally

A very strange problem that I have been struggling with for the past few hours (after solving 5-6 other problems with SFINAE, since I am new to it). Basically in the following code I want to have f()for all possible instances of the templates, but g()is only available when N == 2:

#include <type_traits>
#include <iostream>

template<typename T, int N>
class A
{
public:
    void f(void);
    void g(void);
};

template<typename T, int N>
inline void A<T, N>::f()
{
    std::cout << "f()\n";
}

template<typename T, int N, typename std::enable_if<N == 2, void>::type* = nullptr>
inline void A<T, N>::g()
{
    std::cout << "g()\n";
}

int main(int argc, char *argv[])
{
    A<float, 2> obj;
    obj.f();
    obj.g();

    return 0;
}

When I try to compile it, I get an error message with the presence of three template parameters instead of two. Then, after some testing, I decided to move the definition g()inside the definition itself A, for example:

#include <type_traits>
#include <iostream>

template<typename T, int N>
class A
{
public:
    void f(void);

    template<typename t = T, int n = N, typename std::enable_if<N == 2, void>::type* = nullptr>
    void g()
    {
        std::cout << "g()\n";
    }
};

template<typename T, int N>
inline void A<T, N>::f()
{
    std::cout << "f()\n";
}

int main(int argc, char *argv[])
{
    A<float, 2> obj;
    obj.f();
    obj.g();

    return 0;
}

, . : ? , -, ? : A, ? ? 3 , +1 , A ?

, , , ? , , enable_if, nullptr, , , SO, .

, !!!

+4
2

, , . , "" :

template<typename T, int N>
class A
{
public:
    void f(void);

    template<typename std::enable_if<N == 2, void>::type* = nullptr>
    void g(void);
};

template<typename T, int N>                                            // Class template.
template<typename std::enable_if<N == 2, void>::type* /* = nullptr */> // Function template.
inline void A<T, N>::g()
{
    std::cout << "g()\n";
}

.

[ , , , . , N != 2.]

, .


? . , ?

  • A<T, N>::g() A:

    template<typename T, int N>
    class A
    {
    public:
        void f(void);
        void g(void);
    };
    
    template<typename T, int N, typename std::enable_if<N == 2, void>::type* = nullptr>
    inline void A<T, N>::g()
    {
        std::cout << "g()\n";
    }
    

    A<T, N>::g() A. . , g() , A.

    template<typename T, int N>
    class A
    {
    public:
        void f(void);
    
        // Here...
        template<typename std::enable_if<N == 2, void>::type* = nullptr>
        void g(void);
    };
    
    // And here.
    template<typename T, int N>                                            // Class template.
    template<typename std::enable_if<N == 2, void>::type* /* = nullptr */> // Function template.
    inline void A<T, N>::g()
    {
        std::cout << "g()\n";
    }
    
  • A<T, N>::g() A:

    template<typename T, int N>
    class A
    {
    public:
        void f(void);
    
        template<typename t = T, int n = N, typename std::enable_if<N == 2, void>::type* = nullptr>
        void g()
        {
            std::cout << "g()\n";
        }
    };
    

    , g() , A, . g() :

    // ...
        template<typename T, int N>
        template<typename t = T, int n = N, typename std::enable_if<N == 2, void>::type* = nullptr>
        void g()
        {
            std::cout << "g()\n";
        }
    // ...
    

g() , , . .


, , , SFINAE . , g() SFINAE N N ; , , , A<float, 3>{}.g(). , .

, g(), , N != 2. , SFINAE , ; g() , , SFINAE .

template<typename T, int N>
class A
{
public:
    void f(void);

    // Note the use of "MyN".
    template<int MyN = N, typename std::enable_if<MyN == 2, void>::type* = nullptr>
    void g(void);

    // Note the "fail condition" overload.
    template<int MyN = N, typename std::enable_if<MyN != 2, void>::type* = nullptr>
    void g(void);
};

template<typename T, int N>
template<int MyN /*= N*/, typename std::enable_if<MyN == 2, void>::type* /* = nullptr */>
inline void A<T, N>::g()
{
    std::cout << "g()\n";
}

template<typename T, int N>
template<int MyN /*= N*/, typename std::enable_if<MyN != 2, void>::type* /* = nullptr */>
inline void A<T, N>::g()
{
    std::cout << "()g\n";
}

, , .

template<typename T, int N>
class A
{
public:
    void f(void);

    template<bool B = (N == 2), typename std::enable_if<B, void>::type* = nullptr>
    void g(void);

    template<bool B = (N == 2), typename std::enable_if<!B, void>::type* = nullptr>
    void g(void);
};

// ...

.

+2

A, ( ).
, sfinae , .

g -, sfinae.


:

#include <type_traits>
#include <iostream>

template<typename T, int N>
class A
{
public:
    void f(void);

    template<int M = N>
    std::enable_if_t<M==2> g();
};

template<typename T, int N>
inline void A<T, N>::f()
{
    std::cout << "f()\n";
}

template<typename T, int N>
template<int M>
inline std::enable_if_t<M==2> A<T, N>::g()
{
    std::cout << "g()\n";
}

int main(int argc, char *argv[])
{
    A<float, 2> obj;
    obj.f(); // ok
    obj.g(); // ok (N==2)

    A<double,1> err;
    err.f(); // ok
    //err.g(); invalid (there is no g())

    return 0;
}

, non-type sfinae, .
- template<int M = N> .

.
, , f , g.

+3

Source: https://habr.com/ru/post/1668059/


All Articles