2012-12-17 3 views
8

매개 변수 팩에서 확장 된 인수에서 배열 대 포인터 감쇠를 방지 할 수 있습니까? 예를 들어매개 변수 팩 확장에서 배열 감쇠 방지

:

#include <iostream> 

void foo() { 
    std::cout << "empty\n"; 
} 

template <typename T, typename... Rest> 
void foo(T &&t, Rest... rest) { 
    std::cout << "T, ...\n"; 
    foo(rest...); 
} 

template <typename... Rest> 
void foo(char *p, Rest... rest) { 
    std::cout << "char*, ...\n"; 
    foo(rest...); 
} 

template <int N, typename... Rest> 
void foo(char (&first)[N], Rest... rest) { 
    std::cout << "char[], ...\n"; 
    foo(rest...); 
} 

int main() { 
    char a[2], b[2], c[2]; 
    foo(a, b, c); 
} 

... 출력 :

char[], ... 
char*, ... 
char*, ... 
empty 

당신이 볼 수 있듯이, 첫 번째 통화는 어레이 기반의 과부하로 이동하지만, 후속 호출은 포인터 타입으로 이동 기반 과부하. 모든 호출을 배열 기반 과부하로 전환 할 수있는 방법이 있습니까?

관련 : Problems specializing variable template function

+2

'std :: forward (rest) ...'? – Yakk

답변

7

당신이를 rvalue 참조에 의한 매개 변수 팩을 전달하려는 :

void foo(char (&first)[N], Rest&&... rest) 
           ^^ 

그래서 코드는이 전반적으로 다음과 같습니다

#include <iostream> 

void foo() { 
    std::cout << "empty\n"; 
} 

template <typename T, typename... Rest> 
void foo(T &&t, Rest... rest) { 
    std::cout << "T, ...\n"; 
    foo(rest...); 
} 

template <typename... Rest> 
void foo(char *p, Rest... rest) { 
    std::cout << "char*, ...\n"; 
    foo(rest...); 
} 

template <int N, typename... Rest> 
void foo(char (&first)[N], Rest&&... rest) { 
    std::cout << "char[], ...\n"; 
    foo(rest...); 
} 

int main() { 
    char a[2], b[2], c[2]; 
    foo(a, b, c); 
} 

주는 결과 :

char[], ... 
char[], ... 
char[], ... 
empty 

다른 오버로드가 변경되지 않았지만 일반적으로 (실제로 사용 된 경우) rvalue 참조도 사용하기를 원합니다.

편집 : 왜 이렇게하고 싶습니까? 왜 작동하는지 : rvalue 참조는 rvalue 또는 lvalue에 바인딩 할 수 있습니다. 우리가 여기서 신경 써야 할 중요한 점은 그것이 lvalue에 바인딩 될 때, lvalue로 남아 있다는 것입니다. 배열의 경우 배열로서의 ID를 유지하므로 배열은 수신됩니다.

/값을 기준으로 배열을 전달하면 일반 함수처럼 포인터에 정상적인 "쇠퇴"가 발생합니다.

이 특정 사례의 경우 일반 좌변단 참조를 사용할 수도 있습니다. 그러나 그랬다면 이 아니며 좌현이 ​​아닌 유형의 경우에는이 작동하지 않습니다. 예를 들어 foo(1,2,3);에 전화를 걸었 으면 lvalue 참조를 1, 2 또는 3에 바인딩 할 수 없기 때문에 오류가 발생합니다. 우리가 을 처리하기 위해const 왼쪽 값 참조를 전달할 수 있지만 그 다음에는 rvalue에 대한 참조를 직접 바인딩하지 않을 것입니다. 우리는 전달 된 rvalue의 복사본을 포함하는 임시 데이터를 생성 한 다음 바인딩합니다 그 임시 복사본 대신 lvalue 참조. 특정 int의 경우에는 큰 문제는 아니지만 문제가 될 수있는 복사 비용이 더 많이 든다 (또는 사본이 아닌 원본에 대한 액세스를 원한다면).

+1

"이라고 말하지만 대개 rvalue reference를 사용하기를 원할 것입니다."왜 그렇게하고 싶습니까? 왜 이것이 작동하고 OP는하지 않는가? –

+0

@OlafDietsche : 수정 됨. –

+1

이 훌륭한 설명에 감사드립니다. –

5

@ JerryCoffin의 답변이 이미 그 자리에 올랐지 만, 작은 발언을 추가하고 싶었습니다. 목록 처리 코드를 다음과 같은 항목 1과 분리 할 수 ​​있습니다.

void foo_list() { 
    std::cout << "empty\n"; 
} 

template <typename T, typename... Rest> 
void foo_list(T &&t, Rest&&... rest) { 
    foo(t); 
    foo_list(rest...); 
} 

template <int N> 
void foo(char (&t)[N]){ 
    // ... 
} 

void foo(char *){ 
    // ... 
} 

// etc... 

(어쩌면 이미 그 관용구가 있습니까?).

+0

그게 중요한 포인트입니다. –

관련 문제