개발/C++

C ++ 함수 템플릿 부분 전문화?

MinorMan 2020. 9. 26. 00:45
반응형

<질문>

아래 코드는 클래스의 부분 전문화라는 것을 알고 있습니다.

template  
class MyClass { 
  … 
}; 


// partial specialization: both template parameters have same type 
template  
class MyClass { 
  … 
}; 

또한 C ++에서는 함수 템플릿 부분 전문화를 허용하지 않는다는 것을 알고 있습니다 (전체 만 허용됨). 하지만 내 코드는 하나 / 동일한 유형 인수에 대해 함수 템플릿을 부분적으로 전문화했음을 의미합니까? Microsoft Visual Studio 2010 Express에서 작동하기 때문입니다! 아니라면 부분 전문화 개념을 설명해 주시겠습니까?

#include 
using std::cin;
using std::cout;
using std::endl;

template  
inline T1 max (T1 const& a, T2 const& b) 
{ 
    return a < b ? b : a; 
} 

template  
inline T const& max (T const& a, T const& b)
{
    return 10;
}


int main ()
{
    cout << max(4,4.2) << endl;
    cout << max(5,5) << endl;
    int z;
    cin>>z;
}

<답변1>

기능 부분 전문화는 아직 표준에 따라 허용되지 않습니다. 이 예에서는 실제로 과부하가 걸리고 최대 함수. 그것의 구문은 허용된다면 아래와 같을 것입니다 :

// Partial specialization is not allowed by the spec, though!
template  
inline T const& max (T const& a, T const& b)
{                  ^^^^^ <--- [supposed] specializing here
  return 10;
}

함수 템플릿의 경우 컴파일러 확장을 제외하고 C ++ 표준에서 전체 전문화 만 허용됩니다!


<답변2>

다른 답변에서 지적했듯이 부분 전문화는 허용되지 않으므로 아래와 같이 std :: is_same 및 std :: enable_if를 사용하여 해결할 수 있습니다.

template 
inline typename std::enable_if::value, void>::type
typed_foo(const F& f) {
    std::cout << ">>> messing with ints! " << f << std::endl;
}

template 
inline typename std::enable_if::value, void>::type
typed_foo(const F& f) {
    std::cout << ">>> messing with floats! " << f << std::endl;
}

int main(int argc, char *argv[]) {
    typed_foo("works");
    typed_foo(2);
}

산출:

$ ./a.out 
>>> messing with ints! works
>>> messing with floats! 2

편집 : 남은 다른 모든 사례를 처리 할 수 있어야하는 경우 이미 처리 된 사례가 일치하지 않아야한다는 정의를 추가 할 수 있습니다. 그렇지 않으면 모호한 정의에 빠질 것입니다. 정의는 다음과 같습니다.

template 
inline typename std::enable_if<(not std::is_same::value)
    and (not std::is_same::value), void>::type
typed_foo(const F& f) {
    std::cout << ">>> messing with unknown stuff! " << f << std::endl;
}

int main(int argc, char *argv[]) {
    typed_foo("works");
    typed_foo(2);
    typed_foo("either");
}

다음을 생성합니다.

$ ./a.out 
>>> messing with ints! works
>>> messing with floats! 2
>>> messing with unknown stuff! either

이 모든 경우가 약간 지루해 보이지만 이미 수행 한 모든 작업을 컴파일러에 알려야하므로 최대 5 개 또는 몇 가지 전문화를 처리하는 것이 가능합니다.


<답변3>

전문화 란 무엇입니까?

템플릿을 정말로 이해하고 싶다면 함수형 언어를 살펴 봐야합니다. C ++의 템플릿 세계는 순전히 기능적인 자체 하위 언어입니다.

기능 언어에서는 패턴 일치를 사용하여 선택이 수행됩니다.

-- An instance of Maybe is either nothing (None) or something (Just a)
-- where a is any type
data Maybe a = None | Just a

-- declare function isJust, which takes a Maybe
-- and checks whether it's None or Just
isJust :: Maybe a -> Bool

-- definition: two cases (_ is a wildcard)
isJust None = False
isJust Just _ = True

보시다시피 isJust의 정의에 과부하가 걸립니다.

음, C ++ 클래스 템플릿은 똑같은 방식으로 작동합니다. 매개 변수의 수와 특성을 나타내는 기본 선언을 제공합니다. 선언 일 수도 있고 정의 역할도 할 수 있으며 (원하는 경우) 패턴의 전문화를 제공하고 다른 (그렇지 않으면 어리석은) 버전의 클래스와 연결할 수 있습니다. .

템플릿 함수의 경우 전문화는 다소 어색합니다. 오버로드 해결과 다소 충돌합니다. 따라서 전문화는 비전 문화 버전과 관련이 있으며 과부하 해결 중에는 전문화가 고려되지 않는 것으로 결정되었습니다. 따라서 올바른 기능을 선택하는 알고리즘은 다음과 같습니다.

(심층 치료에 대해서는 GotW # 49 참조)

따라서 기능의 템플릿 전문화는 문자 그대로 두 번째 영역 시민입니다. 내가 생각하는 한, 우리는 그것들 없이는 더 나을 것입니다. 나는 아직 템플릿 전문화 사용이 오버로딩으로 해결 될 수없는 경우를 만나지 못했습니다.

템플릿 전문화입니까?

아니요, 단순히 과부하이며 괜찮습니다. 실제로 오버로드는 일반적으로 예상대로 작동하지만 전문화는 놀라 울 수 있습니다 (제가 링크 한 GotW 기사를 기억하십시오).


<답변4>

비 클래스, 비가 변 부분 전문화는 허용되지 않지만 다음과 같습니다.

컴퓨터 과학의 모든 문제는 다른 수준의 간접적 인 방법으로 해결할 수 있습니다. —— 데이비드 휠러

함수 호출을 전달하기 위해 클래스를 추가하면이 문제를 해결할 수 있습니다. 예는 다음과 같습니다.

template 
struct enable_fun_partial_spec;

struct fun_tag {};

template 
constexpr R fun(Ts&&... ts) {
  return enable_fun_partial_spec::call(
      std::forward(ts)...);
}

template 
struct enable_fun_partial_spec {
  constexpr static R call(Ts&&... ts) { return {0}; }
};

template 
struct enable_fun_partial_spec {
  constexpr static R call(T, T) { return {1}; }
};

template 
struct enable_fun_partial_spec {
  constexpr static R call(int, int) { return {2}; }
};

template 
struct enable_fun_partial_spec {
  constexpr static R call(int, char) { return {3}; }
};

template 
struct enable_fun_partial_spec {
  constexpr static R call(char, T2) { return {4}; }
};

static_assert(std::is_same_v(1, 1)), int>, "");
static_assert(fun(1, 1) == 2, "");

static_assert(std::is_same_v(1, 1)), char>, "");
static_assert(fun(1, 1) == 2, "");

static_assert(std::is_same_v(1L, 1L)), long>, "");
static_assert(fun(1L, 1L) == 1, "");

static_assert(std::is_same_v(1L, 1L)), double>, "");
static_assert(fun(1L, 1L) == 1, "");

static_assert(std::is_same_v(1u, 1)), int>, "");
static_assert(fun(1u, 1) == 0, "");

static_assert(std::is_same_v(1, 'c')), char>, "");
static_assert(fun(1, 'c') == 3, "");

static_assert(std::is_same_v('c', 1)), unsigned>, "");
static_assert(fun('c', 1) == 4, "");

static_assert(std::is_same_v(10.0, 1)), unsigned>, "");
static_assert(fun(10.0, 1) == 0, "");

static_assert(
    std::is_same_v(1, 2, 3, 'a', "bbb")), double>, "");
static_assert(fun(1, 2, 3, 'a', "bbb") == 0, "");

static_assert(std::is_same_v()), unsigned>, "");
static_assert(fun() == 0, "");

<답변5>

예를 들어 std :: swap을 합법적으로 전문화 할 수 있지만 자신의 오버로드를 합법적으로 정의 할 수는 없습니다. 즉, 사용자 정의 클래스 템플릿에 대해 std :: swap 작업을 수행 할 수 없습니다.

오버로딩과 부분 전문화는 경우에 따라 동일한 효과를 가질 수 있지만 모두와는 거리가 멀습니다.


<답변6>

늦은 답변이지만 일부 늦은 독자는 유용하다고 생각할 수 있습니다. 때로는 특수화 될 수 있도록 설계된 도우미 기능도 문제를 해결할 수 있습니다.

그래서 상상해 봅시다. 이것이 우리가 해결하려고 한 것입니다.

template 
void function(X x, Y y)
{
    R* r = new R(x);
    f(r, y); // another template function?
}

// for some reason, we NEED the specialization:
template 
void function(int x, Y y) 
{
    // unfortunately, Wrapper has no constructor accepting int:
    Wrapper* w = new Wrapper();
    w->setValue(x);
    f(w, y);
}

좋아요, 부분 템플릿 함수 전문화, 우리는 그렇게 할 수 없습니다 ... 그래서 전문화에 필요한 부분을 도우미 함수로 "내보내고"그 부분을 전문화하고 사용합시다 :

template 
R* create(T t)
{
    return new R(t);
}
template <>
Wrapper* create(int n) // fully specialized now -> legal...
{
    Wrapper* w = new Wrapper();
    w->setValue(n);
    return w;
}

template 
void function(X x, Y y)
{
    R* r = create(x);
    f(r, y); // another template function?
}

특히 대안 (전문화 대신 정상적인 오버로드, Rubens가 제안한 해결 방법 ... – 이것이 나쁘거나 내 것이 더 낫다는 것이 아니라 다른 하나)가 상당히 많은 공통 코드를 공유 할 경우 특히 흥미로울 수 있습니다.

반응형