사용법을 이해하기 위해 간단한 예제를 얻으려고합니다 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();
}