2014-02-15 1 views
1

C++ 11에서는 decltype을 사용하여 "복잡한"후행 반환 유형으로 선언 된 함수 템플릿을 어떻게 전문화 할 수 있습니까? GCC의 다음 작동하지만 "오류 C2912를 : 명시 적 전문성 'INT f를 (무효)가'함수 템플릿의 특수화 아니다"생산 VC2013에서 :decltype 후행 반환 유형이있는 특수 함수 템플릿

#include <iostream> 

int myint() { return 1; } 

template<class T> 
auto f() -> decltype(myint()) // this seems to cause problems 
{ 
    std::cout << "general\n"; 
    return 1; 
} 

template <> 
auto f<double>() -> decltype(myint()) 
{ 
    std::cout << "special\n"; 
    return 2; 
} 

int main() 
{ 
    f<int>(); 
    f<double>(); // compiler error in VC, but not in GCC 
} 

내가 "복잡한"말을 기술적으로 정확한 부족에 왜냐하면 나는 무엇이 차이를 만드는지 모르겠다. 예를 들어, 사용하여 다음 decltype, 내장 작업이 어떤 함수 결과 유형에 따라하지, 템플릿 특수화와 잘 작동 :

자동 F() - 그래서> decltype (1 + 1)

, 내 질문 (모두 서로 관련이 있음) :

  1. 내 코드가 올바른가요? C++ 11?
  2. 이것은 VC 버그입니까?
  3. 특수화의 종류가 작동하지 않는 경우 변경할 수없는 레거시 컨테이너 클래스에 대해 어떻게 std :: begin 및 std :: end (그래서 범위 기반 for 루프를 제공합니까?)를 전문으로 할 수 있습니까?

답변

5

올바른 코드는 C++ 11입니까?

나에게 맞는 것 같습니다. 또한 gcc와 clang을 모두 -Wall -Wextra으로 깔끔하게 컴파일합니다.

이것은 VC 버그입니까?

가능성이 큽니다. VC는이 점에서 악명이 높습니다. 예를 들어 What exactly is "broken" with Microsoft Visual C++'s two-phase template instantiation? 또는 google msvc two-phase lookup을 참조하십시오.

이런 종류의 특수화가 작동하지 않으면 변경 불가능한 레거시 컨테이너 클래스에 대해 std :: begin 및 std :: end (그래서 범위 기반 for 루프를 제공합니다)를 어떻게 전문화 할 수 있습니까? 사용자가 제공 한 코드에 대한

, 해결 방법은 형식 정의를 사용하는 것입니다 :

#include <iostream> 

int myint() { return 1; } 

typedef decltype(myint()) return_type; 

template<class T> 
return_type f() 
{ 
    std::cout << "general\n"; 
    return 1; 
} 

template <> 
return_type f<double>() 
{ 
    std::cout << "special\n"; 
    return 2; 
} 

int main() 
{ 
    f<int>(); 
    f<double>(); 
} 

세 가지 주요 컴파일러 (GCC, 그 소리, 대)이 코드에 만족하실 것 같습니다.


UPDATE :

을 내가 전문 수있는 방법 std::beginstd::end (및 제공 범위 기반 루프) 전문화 이런 종류의하지 않는 경우 변경할 수 레거시 컨테이너 클래스 작업?
[그리고 코멘트에서] 나는 항상 std::beginstd::end이 최선의 접근 방법이라고 생각했습니다.

std::begin()std::end()을 전문적으로 생각한 후에는 내 마지막 수단이 될 것입니다. 내 첫번째 시도는 회원 begin()end() 기능을 제공하는 것입니다; 유감스럽게도 해당 코드를 수정할 수 없으므로 선택 사항이 아닙니다. 그런 다음, 내 두 번째 시도 내 자신의 네임 스페이스에없는 기능 을 제공하는 것입니다 : 당신이 컨테이너에 대한 std::begin()std::end()을 전문으로 할 수 있다면

#include <iostream> 
#include <initializer_list> 
#include <vector> 

namespace my_namespace { 

template <typename T> class my_container; 
template <typename T> T* begin(my_container<T>& c); 
template <typename T> T* end(my_container<T>& c); 

template <typename T> 
class my_container { 

    public: 

    explicit my_container(std::initializer_list<T> list) : v(list) { } 

    friend T* begin<>(my_container& c); 
    friend T* end<>(my_container& c); 

    private: 

    std::vector<T> v; 
}; 

template <typename T> 
T* begin(my_container<T>& c) { 

    return c.v.data(); 
} 

template <typename T> 
T* end(my_container<T>& c) { 

    return c.v.data()+c.v.size(); 
} 

} 

int main() { 

    my_namespace::my_container<int> c{1, 2, 3}; 

    for (int i : c) 
    std::cout << i << '\n'; 
} 

이 방법은 작업해야합니다. 또한 전역 네임 스페이스에서 수행하는 경우에도 작동합니다 (즉, namespace my_namespace {을 생략하고 }을 닫음). 내 구현을 고유 한 네임 스페이스에 넣는 것을 선호합니다.

는 나에게 확실히 새로운되었으며, 타입 정의 해트트릭도

+0

감사를 참조하십시오! 나는 여전히 C++ 11에서 std :: begin et al과 관련하여 실제로 관용적 인 것을 이해하려고 노력하고있다. 아마 나의 주요 관심사가 범위 기반 for 루프를 제공하는 것이라면, std :: begin/std :: end에 대해 너무 신경 쓰지 말고 독립적 인 비표준 begin/end 함수를 제공해야한다. –

+0

@ChristianHackl 독립형'std :: begin()'과'std :: end()'는 예전의 C 스타일 배열 (예를 들어'int v [42];)에 유용하다. 이전 컨테이너를 새로운 범위 기반 for 루프로 작업하게하려면'my_container.begin()'과'my_container.end()'* member * 함수를 제공하면됩니다. 낡은 컨테이너를 바꿀 수 있다고 가정하고, 당신은 변함없이 쓰고 ... 흠. 또는 이전 코드를 변경할 필요가없는 이전 컨테이너에 대한보기를 정의 할 수 있습니다. 어떤 경우에는, 간단한 typedef가 충분하다면 typedef를 사용할 것이다. – Ali

+0

같은 줄에 뭔가 생각 중입니다 ... LegacyContainerLoopAdapter는 LegacyContainer를 begin()/end() 멤버 함수로 래핑합니다. 하지만 std :: begin/std :: end는 또한 더 높은 수준의 일반화 된 것들에 대한 기본 연산처럼 유용 할 것입니다 (예 : bool empty (T const & c) {return std :: begin (c) = = std :: end (c);}). 그래서 나는 std :: begin과 std :: end를 전문화하는 것이 항상 최선의 방법이라고 생각했다. –