2014-11-03 3 views
24

반환 유형으로 decltype 덕분에 C++ 11에서는 데코레이터를 매우 쉽게 소개 할 수있었습니다. 내가 장식 다른 종류의 여러 번, 내가 먼저 간단하게 base 모든 것을 전달하는 decorator 클래스를 소개 그렇게 때문에왜 자동 리턴 유형이 과부하 해결을 변경합니까?

struct base 
{ 
    void fun(unsigned) {} 
}; 

내가 추가 기능을 장식 할, 그리고 : 예를 들어,이 클래스를 고려하십시오. 실제 코드에서는 std::shared_ptr을 통해 이루어 지므로 장식을 제거하고 "알몸"개체를 복구 할 수 있으며 모든 것이 템플리트 화됩니다.

#include <utility> // std::forward 
struct decorator 
{ 
    base b; 

    template <typename... Args> 
    auto 
    fun(Args&&... args) 
    -> decltype(b.fun(std::forward<Args>(args)...)) 
    { 
    return b.fun(std::forward<Args>(args)...); 
    } 
}; 

완벽한 전달 및 decltype은 훌륭합니다. 실제 코드에서는 실제로 함수의 이름 만 필요한 매크로를 사용합니다. 나머지는 모두 상용구입니다.

그리고, 나는 (derived가 부적절 동의하지만, 상속을 통해하지 불구하고, derived은 뿐인 것을 base의 이해를 도움) 내 객체에 기능을 추가하는 derived 클래스를 도입 할 수 있습니다.

struct foo_t {}; 
struct derived : decorator 
{ 
    using decorator::fun; // I want "native" fun, and decorated fun. 
    void fun(const foo_t&) {} 
}; 

int main() 
{ 
    derived d; 
    d.fun(foo_t{}); 
} 

그런 다음 C++ (14)는 간단한 방법으로 사물을 쓸 수 반환 형식 공제, 함께 온 :에게 다음을

struct decorator 
{ 
    base b; 

    template <typename... Args> 
    auto 
    fun(Args&&... args) 
    { 
    return b.fun(std::forward<Args>(args)...); 
    } 
}; 

그리고 : 전달 함수의 decltype 부분을 제거 휴식. 예, 적어도 GCC와 연타 모두에 따라이 :

template <typename... Args> 
    auto 
    fun(Args&&... args) 
    -> decltype(b.fun(std::forward<Args>(args)...)) 
    { 
    return b.fun(std::forward<Args>(args)...); 
    } 
}; 

이 일치하지 않는 (그리고 문제가되지 autodecltype(auto)입니다) :

template <typename... Args> 
    auto 
    fun(Args&&... args) 
    { 
    return b.fun(std::forward<Args>(args)...); 
    } 
}; 

과부하 해상도는 것 같다 완전히 다른과는 다음과 같이 종료 :

clang++-mp-3.5 -std=c++1y main.cc 
main.cc:19:18: error: no viable conversion from 'foo_t' to 'unsigned int' 
    return b.fun(std::forward<Args>(args)...); 
       ^~~~~~~~~~~~~~~~~~~~~~~~ 
main.cc:32:5: note: in instantiation of function template specialization 
     'decorator::fun<foo_t>' requested here 
    d.fun(foo_t{}); 
    ^
main.cc:7:20: note: passing argument to parameter here 
    void fun(unsigned) {} 
       ^

내가 실패 이해 : 내 전화 (d.fun(foo_t{})가) 서명과 완벽하게 일치하지 않는 경우 derived::funconst foo_t&이므로 매우 열심히 decorator::fun이 맞습니다 (우리는 Args&&...이 완벽하게 일치하지 않는 것을 바인딩하는 데 극도로 성급한 방법을 알고 있습니다). 그래서 foo_t을 처리 할 수없는 base::fun에게 전달합니다.

나는 foo_t 대신 const foo_t&의를 취할 derived::fun을 변경하는 경우

은, 그것은 참으로 여기에 문제가 derived::fundecorator::fun 사이에 경쟁이 있다는 것을 보여주는 예상대로 작동합니다.

그러나 왜 도대체 리턴 유형 공제와 함께이 쇼를합니까 ??? 그리고 더 정확하게는 이위원회에서 선택한 행동입니까?

는 Coliru에, 일을 더 쉽게 만들려면 :

감사합니다!

+0

@Nawaz : 팁 주셔서 감사. 여기서는 차이가 없습니다. – akim

+7

'auto'도'decltype (auto)'도 안되는'decltype (b.fun (std :: forward (args) ...))'는 * SFINAE * 표현식으로 작동합니다. –

+4

Piotr S.의 의견 : 그것은 의도적 인 것입니다. SFINAE가 작동하려면 함수 본문을 보지 않고 반환 유형을 사용할 수 있어야합니다. 리턴 람다를 사용하려면 함수 본문을 보지 않고 반환 유형을 사용할 수 없어야합니다. 람다를 되돌려주는 것이 SFINAE보다 더 중요하다는 결정이 내려졌습니다. – hvd

답변

18

그냥이 호출 보면 : 당신은 함수에 인수로 전달 (즉를 rvalue) 임시을 만들

d.fun(foo_t{}); 

. 자, 어떻게 생각하십니까?

  • 그것은 그것으로 인해 다시 foo_t로 인해 unsigned int로 변환 할 수 없습니다 무효 반환 형식 공제 (에를 rvalue 하지만을 수용 할 수있는 시도이, 인수 Arg&&에 결합하기 때문에 처음에있는 b.fun(std::forward<Args>(args)...) 턴 잘못된 표현이 될 수 있음),이 경우 SFINAE가 그림과 같이 반환 유형으로 decltype(expr)을 사용하면이 함수는 거부됩니다. 그러나 단순히 auto을 사용하면 SFINAE는 그림에 나타나지 않으며 오류는 컴파일 오류가 발생하는 하드 오류로 분류됩니다.

  • SFINAE가 첫 번째 경우에 작동하면 인수로 foo_t const&을 허용하는 두 번째 오버로드가 호출됩니다.

+1

아, C++ 11이'base :: fun'에 대한 호출이 거부되었고 _then_'derived :: fun'가 두 번째 선택으로 받아 들여 졌기 때문에 깨닫지 못했습니다. 고마워요! 그러나'decltype (auto)'로 문제가 해결되지 않는 이유는 무엇입니까? 그것은 GCC와 Clang 모두에서 버그로 간주되어야 하는가? – akim

+1

좋아, 그냥 질문 자체에 귀하의 의견을 읽고, 그 모든 것을 명확하게, 많은 감사합니다! – akim