멤버 함수를 조건부로 컴파일하기위한 std :: enable_if 노력한 후에 너희들에게 내

사용법을 이해하기 위해 간단한 예제를 얻으려고합니다 std::enable_if. 이 답변을 읽은 후 간단한 예를 제시하기가 너무 어렵지 않아야한다고 생각했습니다. std::enable_if두 멤버 함수 중에서 선택하고 그중 하나만 사용할 수 있도록 사용하고 싶습니다 .

불행히도, 다음은 gcc 4.7로 컴파일되지 않으며 몇 시간 동안 노력한 후에 너희들에게 내 실수가 무엇인지 묻고있다.

#include <utility>
#include <iostream>

template< class T >
class Y {

    public:
        template < typename = typename std::enable_if< true >::type >
        T foo() {
            return 10;
        }
        template < typename = typename std::enable_if< false >::type >
        T foo() {
            return 10;
        }

};


int main() {
    Y< double > y;

    std::cout << y.foo() << std::endl;
}

gcc는 다음과 같은 문제를보고합니다.

% LANG=C make CXXFLAGS="-std=c++0x" enable_if
g++ -std=c++0x    enable_if.cpp   -o enable_if
enable_if.cpp:12:65: error: `type' in `struct std::enable_if<false>' does not name a type
enable_if.cpp:13:15: error: `template<class T> template<class> T Y::foo()' cannot be overloaded
enable_if.cpp:9:15: error: with `template<class T> template<class> T Y::foo()'

왜 g ++가 두 번째 멤버 함수에 대한 잘못된 인스턴스를 삭제하지 않습니까? 표준에 따르면 std::enable_if< bool, T = void >::type부울 템플릿 매개 변수가 true 인 경우에만 존재합니다. 그러나 왜 g ++는 이것을 SFINAE로 간주하지 않습니까? 오버로드 오류 메시지는 g ++이 두 번째 멤버 함수를 삭제하지 않으며 이것이 오버로드되어야한다고 믿는 문제에서 비롯된 것으로 생각합니다.



답변

SFINAE는 템플리트 인수의 인수 공제를 대체하여 구문이 잘못된 경우에만 작동합니다. 그러한 대체는 없습니다.

나도 그 생각하고 사용하려 std::is_same< T, int >::value하고 ! std::is_same< T, int >::value있는이 같은 결과를 제공합니다.

클래스 템플릿이 인스턴스화 될 때 ( Y<int>다른 경우 중 유형의 객체를 만들 때 발생 ) 모든 멤버 선언을 인스턴스화해야합니다 (정의 / 본문은 아닙니다!). 그중에는 멤버 템플릿도 있습니다. T그때 알려진 사실 이며 !std::is_same< T, int >::value거짓이됩니다. 그래서 그것은 Y<int>포함 하는 클래스 를 만들 것입니다

class Y<int> {
    public:
        /* instantiated from
        template < typename = typename std::enable_if<
          std::is_same< T, int >::value >::type >
        T foo() {
            return 10;
        }
        */

        template < typename = typename std::enable_if< true >::type >
        int foo();

        /* instantiated from

        template < typename = typename std::enable_if<
          ! std::is_same< T, int >::value >::type >
        T foo() {
            return 10;
        }
        */

        template < typename = typename std::enable_if< false >::type >
        int foo();
};

std::enable_if<false>::type그 선언이 잘못 형성되도록, 존재하지 않는 유형에 액세스합니다. 따라서 프로그램이 유효하지 않습니다.

enable_if멤버 템플릿 자체가 멤버 템플릿의 매개 변수에 종속 되도록해야 합니다. 그러면 전체 유형이 여전히 종속적이므로 선언이 유효합니다. 이 중 하나를 호출하려고하면 템플릿 인수에 대한 인수 공제가 발생하고 SFINAE가 예상대로 발생합니다. 이 질문 과 그에 대한 답변을 참조하십시오 .


답변

나는이 짧은 예를 만들었습니다.

#include <iostream>
#include <type_traits>

class foo;
class bar;

template<class T>
struct is_bar
{
    template<class Q = T>
    typename std::enable_if<std::is_same<Q, bar>::value, bool>::type check()
    {
        return true;
    }

    template<class Q = T>
    typename std::enable_if<!std::is_same<Q, bar>::value, bool>::type check()
    {
        return false;
    }
};

int main()
{
    is_bar<foo> foo_is_bar;
    is_bar<bar> bar_is_bar;
    if (!foo_is_bar.check() && bar_is_bar.check())
        std::cout << "It works!" << std::endl;

    return 0;
}

내가 자세히 설명하고 싶은 경우 의견을 말하십시오. 나는 코드가 다소 자명하다고 생각하지만 다시 한 번 틀렸다. 🙂

여기 에서 실제로 볼 수 있습니다 .


답변

“작동하는”솔루션을 찾고있는 후발 자들에게 :

#include <utility>
#include <iostream>

template< typename T >
class Y {

    template< bool cond, typename U >
    using resolvedType  = typename std::enable_if< cond, U >::type;

    public:
        template< typename U = T >
        resolvedType< true, U > foo() {
            return 11;
        }
        template< typename U = T >
        resolvedType< false, U > foo() {
            return 12;
        }

};


int main() {
    Y< double > y;

    std::cout << y.foo() << std::endl;
}

다음과 같이 컴파일하십시오.

g++ -std=gnu++14 test.cpp 

러닝은 다음을 제공합니다.

./a.out
11

답변

에서 게시물 :

기본 템플릿 인수는 템플릿 서명의 일부가 아닙니다

그러나 다음과 같이 할 수 있습니다.

#include <iostream>

struct Foo {
    template < class T,
               class std::enable_if < !std::is_integral<T>::value, int >::type = 0 >
    void f(const T& value)
    {
        std::cout << "Not int" << std::endl;
    }

    template<class T,
             class std::enable_if<std::is_integral<T>::value, int>::type = 0>
    void f(const T& value)
    {
        std::cout << "Int" << std::endl;
    }
};

int main()
{
    Foo foo;
    foo.f(1);
    foo.f(1.1);

    // Output:
    // Int
    // Not int
}

답변

이 문제를 해결하는 한 가지 방법은 멤버 함수의 특수화를 다른 클래스에 특수화 한 다음 해당 클래스에서 상속하는 것입니다. 다른 모든 기본 데이터에 액세스하려면 상속 순서를 변경해야 할 수도 있지만이 기술은 작동합니다.

template< class T, bool condition> struct FooImpl;
template<class T> struct FooImpl<T, true> {
T foo() { return 10; }
};

template<class T> struct FoolImpl<T,false> {
T foo() { return 5; }
};

template< class T >
class Y : public FooImpl<T, boost::is_integer<T> > // whatever your test is goes here.
{
public:
    typedef FooImpl<T, boost::is_integer<T> > inherited;

    // you will need to use "inherited::" if you want to name any of the 
    // members of those inherited classes.
};

이 기술의 단점은 다른 멤버 함수에 대해 많은 다른 것들을 테스트해야 할 경우 각 클래스마다 클래스를 만들고 상속 트리에 연결해야한다는 것입니다. 공통 데이터 멤버에 액세스하는 경우에 해당됩니다.

전의:

template<class T, bool condition> class Goo;
// repeat pattern above.

template<class T, bool condition>
class Foo<T, true> : public Goo<T, boost::test<T> > {
public:
    typedef Goo<T, boost::test<T> > inherited:
    // etc. etc.
};

답변

부울은 추론되는 템플릿 매개 변수에 의존해야합니다. 따라서 수정하는 쉬운 방법은 기본 부울 매개 변수를 사용하는 것입니다.

template< class T >
class Y {

    public:
        template < bool EnableBool = true, typename = typename std::enable_if<( std::is_same<T, double>::value && EnableBool )>::type >
        T foo() {
            return 10;
        }

};

그러나 멤버 함수를 오버로드하려는 경우에는 작동하지 않습니다. 대신 Tick 라이브러리 TICK_MEMBER_REQUIRES에서 사용하는 것이 가장 좋습니다 .

template< class T >
class Y {

    public:
        TICK_MEMBER_REQUIRES(std::is_same<T, double>::value)
        T foo() {
            return 10;
        }

        TICK_MEMBER_REQUIRES(!std::is_same<T, double>::value)
        T foo() {
            return 10;
        }

};

또한 다른 라이브러리를 사용하지 않으려는 경우를 위해 자신의 멤버에 다음과 같은 매크로가 필요합니다.

template<long N>
struct requires_enum
{
    enum class type
    {
        none,
        all
    };
};


#define MEMBER_REQUIRES(...) \
typename requires_enum<__LINE__>::type PrivateRequiresEnum ## __LINE__ = requires_enum<__LINE__>::type::none, \
class=typename std::enable_if<((PrivateRequiresEnum ## __LINE__ == requires_enum<__LINE__>::type::none) && (__VA_ARGS__))>::type

답변

다음은 매크로를 사용한 미니멀리즘 예입니다. enable_if((...))더 복잡한 표현식을 사용할 때는 이중 괄호 를 사용 하십시오 .

template<bool b, std::enable_if_t<b, int> = 0>
using helper_enable_if = int;

#define enable_if(value) typename = helper_enable_if<value>

struct Test
{
     template<enable_if(false)>
     void run();
}