2013-07-02 3 views
6

템플릿 기능이 있다고 가정 해 봅시다. assign(). 그것은 포인터 값을 취하고 포인터의 목표에 값을 할당 : 난 항상 T는 첫 번째 인수에서 추론 할 할이 경우템플릿 매개 변수 공제에 하나의 인수를 사용합니까?

template <typename T> void assign(T *a, T b) { *a = b; } 

int main() { 
    double i; 
    assign(&i, 2); 
} 

,하지만 좋은 일을하지 않은 것 같습니다 이것을 표현하는 것. 2의 유형은 그래서, int입니다 :

deduce.cpp:5:5: error: no matching function for call to 'assign' 
    assign(&i, 2); 
    ^~~~~~ 
deduce.cpp:1:28: note: candidate template ignored: deduced conflicting types for parameter 'T' ('double' vs. 'int') 
template void assign(T *a, T b) { *a = b; }

는 두 번째 인수는 템플릿 매개 변수 공제에 참여하지 않도록 내가 assign()를 선언 할 수있는 방법이 있나요?

+0

그래서, 하나의 문제점은 비효율적이다. 'T'가'std :: vector'라고 가정합니다. 인수'b'는 값에 의해 취해진 다음,'a'에 복사 (이동되지 않음)됩니다. 작은 개선은'assign '의 구현을'* a = std :: move (b)'로 바꾸는 것일 수 있습니다. 원시 타입은 비용이 들지 않으며 복잡한 타입은 많이 절약 할 수 있습니다. 커다란 개선은 앞으로 'b'를 완벽하게하는 것입니다. – Yakk

+0

@Yakk 전적으로 동의 함 - 방금 같은 유형의 포인터와 값을 취하는 함수의 예제로 작성했습니다. 실제로는 프리미티브 만 사용하고이 사람보다 더 유용합니다 :). – s4y

답변

11

: UT으로 변환하지 않을 때 설정, 당신은 더 좋은 컴파일 오류가 발생하는 assign() 내부의 정적 주장을 할 수 있습니다 아마 최선의 선택이지만, 당신이 정말로 첫 번째 인수에서 공제를 수행 할 경우, 단순히 두 번째 비 추론 될 수 있도록 :

template<typename T> 
void assign(T* a, typename std::identity<T>::type b); 

이전 버전의이 대답은 C++ 11에 도입 된 템플릿 별칭 기능을 사용하여 제안했습니다. 그러나 템플리트 별칭은 여전히 ​​추측 할 수있는 맥락입니다. std::identitystd::remove_reference이 공제를 방지하는 가장 큰 이유는 템플릿 클래스를 특수화 할 수 있다는 것입니다. 따라서 템플릿 유형 매개 변수의 typedef를 갖고 있어도 다른 유형의 typedef가 동일한 유형일 수 있습니다. 가능한 모호성 때문에 공제가 이루어지지 않습니다. 그러나 템플릿 별칭은 전문화를 배제하므로 여전히 공제가 발생합니다.

+0

+1. 질문을 편집하면 (필자가 놓친) 이것이 올바른 대답 인 것 같습니다. –

+0

이것은 정말 멋진 답변이지만 나에게는 도움이되지 않습니다. 내 컴파일러에서 같은 "충돌 유형"오류가 발생합니다 : ideone.com/GIJbj1 – s4y

+0

'std :: remove_reference'와 비슷합니다 (다소 읽기 쉽지 않은 경우)도 같습니다. – s4y

3

2 값이 유형으로 추론됩니다.이 값은 &i에 의해 유도 된 템플릿 매개 변수와 일치하지 않습니다.

assign(&i, 2.0); 
+0

알아. 그건 작동하지만 부서지기 쉽고 추한 것입니다.''내가 부호없는 long이면''2ul'이되어야하고, float이라면'(float) 2' 등이되어야합니다. 실제 코드에서는 , 그것은 약간의 논쟁을 필요로하고 불쾌한 빨리된다. – s4y

+0

@ 시드 니쉬 그렇다면 앤디의 대답과 함께 가고 싶을 것입니다. – 0x499602D2

2

왜 그냥 독립적 인 매개 변수 유형, 소스 하나와 대상 하나를 사용 : 당신은 이중으로 값을 사용할 필요가?

template <typename D, typename S> void assign(D *a, S b) { *a = b; } 

int main(int argc, char* argv[]) 
{ 
    double i; 
    assign(&i, 2); 
    return 0; 
} 

할당이 불가능한 경우 템플릿 인스턴스화가 컴파일되지 않습니다.

+1

홀수는 소스에'D'를 사용하고 대상에'S'를 사용합니다. –

+0

@Ben : 사실 :) 그냥 스왑했습니다. – Vlad

+0

이렇게 할 계획이라면'S'를 완벽하게 전달하지 않을까요? – Yakk

4

문제는 컴파일러가 첫 번째 및 두 번째 인수에서 충돌 정보를 추론하고 있다는 것입니다. 첫 번째 인자로부터, (idouble)이다 Tdouble이어야 추론, 두 번째로, 그것은 intT를 추론 (2의 종류) int이다.

현재 두 가지 가능성이 있습니다

항상
  • 것은 당신의 인수의 유형에 대한 명시 :

    assign(&i, 2.0); 
    //   ^^^^ 
    
  • 또는 함수 템플릿 템플릿 매개 변수를 사용할 수 있습니다 :

    template <typename T, typename U> 
    void assign(T *a, U b) { *a = b; } 
    

    이 경우 U 해상도를 오버로드 partecipate하지 않도록이 경우는 SFINAE - 제약에 템플릿을 할 수 없습니다 컨버터블 T에 :

    #include <type_traits> 
    
    template <typename T, typename U, 
        typename std::enable_if< 
         std::is_convertible<U, T>::value>::type* = nullptr> 
    void assign(T *a, U b) { *a = b; } 
    

    당신은 과부하에서 함수를 제외 할 필요가없는 경우 두 유형 매개 변수를 사용하여

    #include <type_traits> 
    
    template<typename T, typename U> 
    void assign(T *a, U b) 
    { 
        static_assert(std::is_convertible<T, U>::value, 
         "Error: Source type not convertible to destination type."); 
    
        *a = b; 
    } 
    
+0

변환 할 수없는 경우에 다른 과부하가 없으므로 함수 내에서'static_assert'를 사용하십시오. – Praetorian

+0

@Praetorian : 아니면 그렇습니다. 함수가 작고 오류 메시지가 어쨌든 이해할 수 있기 때문에 나는 그것에 대해 생각하지 않았습니다. 그러나 당신이 맞습니다. 옵션입니다. –

+0

늦은 편집에 대해 죄송합니다. 이것은 꽤 합법적 인 대답입니다. Upvoted, 적어도. – s4y

0

또는 decltype을 사용하여 두 번째 인수를 첫 번째 유형으로 유형 변환 할 수 있습니다.

template<typename T, typename U> 
typename std::enable_if< std::is_convertible< U&&, T >::value >::type // not quite perfect 
assign(T* dest, U&& src) { 
    *dest = std::forward<U>(src); 
} 

두 번째 인수 당신이 T로 변환 수있는 일이지만, 우리는 보편적 인 기준에 의해 받아 조건부 *dest로 이동 :

template <typename T> void assign(T *a, T b) { *a = b; } 

int main() { 
    double i; 
    assign(&i, (decltype(i))2); 
} 
+0

네,하지만 전화 할 때마다 그렇게해야한다는 것을 기억해야합니다. 함수를 개선하려고 할 때 인자로 여러 숫자를 사용했기 때문에이 질문을 게시했습니다. – s4y

1

내 시도는 다음과 같이 보일 것입니다. 필자는 시체를 컴파일하는 데 실패하는 것보다는 서명에서 변환 가능성을 테스트합니다. 왜냐하면 과부하를 찾지 못하는 오류는 몸을 컴파일하지 못하는 것보다 더 정중 해 보입니다.

Live example. 비교

간단한 :

template<typename T> 
void assign(T* dest, typename std::identity<T>::type src) { 
    *dest = std::move(src); 
} 

위의 1 move을 절약 할 수 있습니다.클래스를 옮기기에 비용이 많이 드는 클래스 나 복사 전용이고 복사하는 데 비용이 많이 드는 클래스가 있으면 상당한 금액을 절약 할 수 있습니다.

0

은 분명히 std::identity 더 이상이 (Is there a reason why there is not std::identity in the standard library?)

하지 않습니다하지만 당신은 매개 변수 유형 목록에서 매개 변수 유형을 지정할 수 있습니다 함수 호출 할 때이 방법으로

template <typename T> void assign(T *a, T b) { *a = b; } 

int main() { 
    double i; 
    assign<double>(&i, 2); 
} 

을 컴파일러는 변환합니다 인수 공제없이 함수 템플리트와 일치하도록 두 배로하는 정수 입력 인수.

Live demo

관련 문제