2013-03-17 1 views
6

"static if"와 같은 구조를 수행하기 위해 템플릿 클래스에서 로컬 클래스를 사용하고 싶습니다. 하지만 gcc 4.8이 제 코드를 컴파일하고 싶지 않은 문제에 직면했습니다. 그러나 4.7 않습니다.C++ GCC이 sfinae 코드는 GCC 4.7로 컴파일 할 수 있지만 4.8로 컴파일 할 수없는 이유는 무엇입니까?

이 샘플 :

#include <type_traits> 
#include <iostream> 
#include <string> 

using namespace std; 

struct A { 
    void printA() { 
     cout << "I am A" << endl; 
    } 
}; 
struct B { 
    void printB() { 
     cout << "I am B" << endl; 
    } 
}; 

template <typename T> 
struct Test { 
    void print() { 
     struct IfA { 
      constexpr IfA(T &value) : value(value) { 
      } 
      T &value; 
      void print() { 
       value.printA(); 
      } 
     }; 
     struct IfB { 
      constexpr IfB(T &value) : value(value) { 
      } 
      T &value; 
      void print() { 
       value.printB(); 
      } 
     }; 
     struct Else { 
      constexpr Else(...) {} 
      void print() { 
      } 
     }; 
     typename conditional<is_same<T, A>::value, IfA, Else>::type(value).print(); 
     typename conditional<is_same<T, B>::value, IfB, Else>::type(value).print(); 
    } 
    T value; 
}; 

int main() { 
    Test<A>().print(); 
    Test<B>().print(); 
} 

옵션 :

g++ --std=c++11 main.cc -o local-sfinae 

작업 :

  1. 인쇄를위한 서로 다른 인터페이스와 클래스 A와 B 감안할.
  2. A와 B를 모두 인쇄 할 수있는 일반 클래스 테스트를 작성하십시오.
  3. 네임 스페이스 또는 클래스 범위를 오염시키지 마십시오. 코드의

설명 :

  1. 이는 깨끗한 예입니다.
  2. 나는 "정적 인 경우"의 구조를 일반화하기 때문에 이와 같은 접근법을 사용합니다. 인자를 IfA와 IfB 클래스에 필드를 통해 전달하고 print() 함수로 직접 전달하지 않는다.
  3. 필자는 그러한 구성을 많이 사용합니다.
  4. 이러한 구성은 (오염 된) 클래스 범위에 있으면 안된다는 것을 발견했습니다. 나는 그들이 메소드 범위에 있어야한다는 것을 의미합니다.

그래서 질문입니다.

이 코드는 GCC 4.8로 컴파일 할 수 없습니다. 왜냐하면 결코 사용되지 않더라도 모든 클래스를 검사하기 때문입니다. 하지만 바이너리로 인스턴스화하지는 못했습니다 (오류를 일으키는 행을 주석 처리하고 gcc 4.8을 사용하여 컴파일했습니다). 증명 :

$ nm local-sfinae |c++filt |grep "::If.*print" 
0000000000400724 W Test<A>::print()::IfA::print() 
00000000004007fe W Test<B>::print()::IfB::print() 

참조 : Test :: print() :: IfB :: print()가 없습니다. (후에 참조 : ') (보이드 테스트 :: 인쇄 :: IFB : 인쇄()과 T = A])

오류를 I는 GCC 4.8 상기 코드를 컴파일하는 경우 :

g++ --std=c++11 main.cc -o local-sfinae 
main.cc: In instantiation of 'void Test<T>::print()::IfB::print() [with T = A]': 
main.cc:36:9: required from 'void Test<T>::print() [with T = A]' 
main.cc:49:21: required from here 
main.cc:34:17: error: 'struct A' has no member named 'printB' 
       value.printB(); 
       ^
main.cc: In instantiation of 'void Test<T>::print()::IfA::print() [with T = B]': 
main.cc:28:9: required from 'void Test<T>::print() [with T = B]' 
main.cc:50:21: required from here 
main.cc:26:17: error: 'struct B' has no member named 'printA' 
       value.printA(); 
       ^
  1. GCC 4.8 버그입니까?
  2. 아니면 GCC 4.7 버그입니까? 어쩌면 코드를 컴파일해서는 안됩니다.
  3. 또는 내 버그이며, 컴파일러 동작에 의존해서는 안됩니다. "정적 if"를 구현하기 위해 이러한 접근 방식을 사용하지 않아야합니다.

추가 정보 :

이 간단한 코드는 4.7에서 컴파일,하지만 4.8. 나는 그것을 단축했다.

struct A { 
    void exist() { 
    } 
}; 

template <typename T> 
struct Test { 
    void print() { 
     struct LocalClass { 
      constexpr LocalClass(T &value) : value(value) { 
      } 
      T &value; 
      void print() { 
       value.notExist(); 
      } 
     }; 
    } 
    T value; 
}; 

int main() { 
    Test<A>().print(); 
} 

오류 : 2012.10과 2013.02 :

main.cc: In instantiation of 'void Test<T>::print()::LocalClass::print() [with T = A]': 
main.cc:16:9: required from 'void Test<T>::print() [with T = A]' 
main.cc:22:21: required from here 
main.cc:14:17: error: 'struct A' has no member named 'notExist' 
       value.notExist(); 
       ^

두 GCC 4.8 버전을 테스트했습니다. GCC 4.8 버그가 있기를 바래 고칠 수 있습니다.

+0

"진단 필요 없음"이 여전히 유효하지 않다고 말하는 표준에 대한 잘못된 코드입니다. 따라서 컴파일러는 컴파일을 거부 할 수 있습니다. –

답변

3

LocalClass은 템플릿이 아닙니다. "사용되지 않은 경우 인스턴스화되지 않음"규칙은 클래스 템플릿의 멤버 함수에만 적용됩니다.

즉, Test::print()이 인스턴스화되면 내부 클래스의 사용되지 않은 멤버를 포함하여 내부의 모든 요소가 활성화됩니다.

3

코드에 SFINAE가 없습니다.

SFINAE 템플릿 인수를 공제와 인수 대체 (SFINAE가 대체 의미에서 'S')하지만시 적용 실패하지 않습니다 Test의 템플릿 매개 변수 목록에 T에 대한 A를 대체 할 때 프로그램의 유일한 대체 발생 .

그런 다음 을 호출하여 어떤 대체도 포함하지 않고 이 유효하지 않으므로 오류가 발생합니다. Test<A>::print()을 인스턴스화합니다.

SFINAE는 함수 호출로 인한 템플릿 인수 공제 또는 기본 인수로 템플릿 매개 변수를 추론하는 경우와 같은 대체 컨텍스트에서 사용해야합니다.

+0

나는 에 많은 sfinae 코드가 있음을 의미합니다. 그러나 GCC 4.7도 4.8도 실제로 LocalClass를 인스턴스화하지 않는 것으로 보입니다. 그러나 GCC 4.8 또한 오류를 반환합니다. 당신의 의견에 감사드립니다. 나는 더 작은 타입의 스위칭 메커니즘을 만들었다 : http://ideone.com/v1KmTA – cppist

+0

@n.m. 다른 답변에서'LocalClass'는 템플릿이 아니므로 인스턴스화 할 수 없다고 말합니다. 정의가 유효하지 않습니다. 주변 함수 템플릿이 인스턴스화 될 때 진단됩니다. –