2017-09-22 16 views
0

일부 코드를 디버깅하는 동안 복잡한 유형의 객체 값을 텍스트 파일에 덤프하여 클래스와 작동하는 경우를 비교할 수있는 클래스를 만들었습니다. 그렇지 않습니다. 나는 (베어 예로 감소)이 같은 클래스를 구현 :자동 템플릿 유형 공제 복잡한 포인터 및 참조

#include <iostream> 

class someOtherClass 
{ 
public: 
    someOtherClass() 
     : a(0) 
     , b(1.0f) 
     , c(2.0) 
    {} 
    int a; 
    float b; 
    double c; 
}; 

class logger 
{ 
public: 
    // Specific case for handling a complex object 
    logger& operator << (const someOtherClass& rObject) 
    { 
     std::cout << rObject.a << std::endl; 
     std::cout << rObject.b << std::endl; 
     std::cout << rObject.c << std::endl; 
     return *this; 
    } 

    // [other class specific implementations] 

    // Template for handling pointers which might be null 
    template< typename _T > 
    logger& operator << (const _T* pBar) 
    { 
     if (pBar) 
     { 
      std::cout << "Pointer handled:" << std::endl; 
      return *this << *pBar; 
     } 
     else 
      std::cout << "null" << std::endl; 
     return *this; 
    } 

    // Template for handling simple types. 
    template< typename _T > 
    logger& operator << (const _T& rBar) 
    { 
     std::cout << "Reference: " << rBar << std::endl; 
     return *this; 
    } 
}; 

int main(int argc, char* argv[]) 
{ 
    logger l; 
    someOtherClass soc; 
    someOtherClass* pSoc = &soc; 
    l << soc; 
    l << pSoc; 
    pSoc = nullptr; 
    l << pSoc; 
    return 0; 
} 

나는 다음과 같은 출력을 얻기 위해 기다리고 있었다 :

0 
1 
2 
Pointer handled: 
0 
1 
2 
null 

하지만 실제로 얻을 것은 :

0 
1 
2 
Reference: 010AF7E4 
Reference: 00000000 

자동 구현 유형 공제가 참조 구현을 선택하고 포인터 구현을 선택하는 대신 유형을 someOtherClass*으로 설정하는 것 같습니다. 이 때문에 필요

template< typename _T , typename = typename ::std::enable_if_t<!std::is_pointer<_T>::value> > 
logger& operator << (const _T& rBar) 
{ 
    std::cout << "Reference: " << rBar << std::endl; 
    return *this; 
} 

Online compiler

: 내가 제대로이 템플릿을 사용하기 위해서, 그래서 포인터 유형이 될 수 logger& operator << (const _T& rBar) 유형 T에서 비주얼 스튜디오 2012

+0

당신은 [mcve] –

+0

@PasserBy 필요합니다 제공 한 코드가 자체 포함하고 그것은 완전하고 검증 가능하지만, 최소한 아닌 @Mat 문제 – Mat

+0

을 재생합니다. 이것은 약간 까다롭게 들리지만 솔직히 그것이 모든 관계자들이 소비 한 시간을 줄이는 데 도움이된다고 생각합니다. 문제를 최소한으로 줄이는 저자는 다른 사람보다 빠릅니다. –

답변

0

를 사용하고있어 일부 제한이 필요 템플릿이 인스턴스화 될 때 const _T & pBar_T = someOtherClass * 변형이있는 경우이 경우 필요한 변환 시퀀스는 신원 변환으로 간주되는 참조 바인딩 만 포함하고 변형은입니다.은 복사 초기화를 수반합니다.

+2

당신은 const 포인터가 const 포인터보다 과부하 해상도 –

+0

에서 왜 선택 되었는지를 추가해야합니다. 설명과 잠재적 솔루션을 가져 주셔서 감사합니다.불행히도 VS2012는 완전한 C++ 11 구현이 없으며 함수 정의에 기본 유형을 지정할 수 없습니다 (템플릿 클래스에서만). 제한을 추가하는 또 다른 방법이 있습니까? –

+1

포인터가 const가 아닐 때 참조 오버로드보다 T *를 받아 들일 수있는 다른 오버로드를 추가 할 수 있습니다. –

0

다음은이 로깅 클래스가 커지고 더 성숙 해지면 도움이 될 수있는 몇 가지 수정 사항과 특수 효과입니다.

가) 잘못된 유형 공제의 초기 문제를 해결 :

내가하려고했습니다.

b) 기록되는 항목에서 로거를 분리하십시오. 그렇지 않으면 로거가 전체 애플리케이션 및 모든 라이브러리에 대해 알아야합니다.

c) 제 3 자 라이브러리에서 제공 한 경우에도 모든 유형의 로깅을 쉽게 허용하는 메커니즘을 제공합니다.

#include <iostream> 

// I've put the logger and its helpers into a namespace. This will keep code tidy and help with 
// ADL. 
namespace logging 
{ 
    // define a general function which writes a value to a stream in "log format". 
    // you can specialise this for specific types in std:: if you wish here 
    template<class T> 
    void to_log(std::ostream& os, T const& value) 
    { 
     os << value; 
    } 

    // define a general function objects for writing a log-representation of tyoe T. 
    // There are 2 ways to customise this. 
    // a) provide a free function called to_log in the same namespace as your classes (preferred) 
    // b) specialise this class. 
    template<class T> 
    struct log_operation 
    { 
     void operator()(std::ostream& os, T const& value) const 
     { 
      to_log(os, value); 
     } 
    }; 

    // specialise for any pointer 
    template<class T> 
    struct log_operation<T*> 
    { 
     void operator()(std::ostream& os, T* ptr) const 
     { 
      if (!ptr) 
       os << "null"; 
      else 
      { 
       os << "->"; 
       auto op = log_operation<std::decay_t<T>>(); 
       op(os, *ptr); 
      } 
     } 
    }; 

    // the logger is now written in terms of log_operation() 
    // it knows nothing of your application's types 
    class logger 
    { 
    public: 

     // Template for handling any type. 
     // not that this will also catch pointers. 
     // we will disambiguate in the log_operation 
     template< typename T > 
     logger& operator << (const T& rBar) 
     { 
      auto op = log_operation<std::decay_t<T>>(); 
      op(std::cout, rBar); 
      std::cout << std::endl; 
      return *this; 
     } 
    }; 
} 

class someOtherClass 
{ 
public: 
    someOtherClass() 
     : a(0) 
     , b(1.0f) 
     , c(2.0) 
    {} 
    int a; 
    float b; 
    double c; 
}; 

// someOtherClass's maintainer provides a to_log function 
void to_log(std::ostream& os, someOtherClass const& c) 
{ 
    os << "someOtherClass { " << c.a << ", " << c.b << ", " << c.c << " }"; 
} 

namespace third_party 
{ 
    // the is in a 3rd party library. There is no to_log function and we can't write one which will be found with 
    // ADL... 
    struct classWhichKnowsNothingOfLogs {}; 
} 

/// ..so we'll specialise in the logging namespace 

namespace logging 
{ 
    template<> 
    struct log_operation<::third_party::classWhichKnowsNothingOfLogs> 
    { 
     void operator()(std::ostream& os, ::third_party::classWhichKnowsNothingOfLogs const& value) const 
     { 
      os << "classWhichKnowsNothingOfLogs {}"; 
     } 
    }; 
} 


int main(int argc, char* argv[]) 
{ 
    logging::logger l; 
    someOtherClass soc; 
    someOtherClass* pSoc = &soc; 
    l << soc; 
    l << pSoc; 
    pSoc = nullptr; 
    l << pSoc; 

    l << third_party::classWhichKnowsNothingOfLogs(); 
    return 0; 
} 

예상 출력 :

someOtherClass { 0, 1, 2 } 
->someOtherClass { 0, 1, 2 } 
null 
classWhichKnowsNothingOfLogs {}