2017-09-22 2 views
0

Während versuchen, einen Code zu debuggen, habe ich eine Klasse, um die Werte einer komplizierten Hierarchie von Objekten in eine Textdatei zu entleeren, so dass ich einen Fall vergleichen, wo es gegen einen Fall funktioniert, wo es tut es nicht. Ich implementiert die Klasse wie folgt (auf ein bloßes Beispiel reduziert):automatische Vorlagentyp Abzug verwirrend Zeiger und Referenzen

#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; 
} 

Ich hatte erwartet, die folgende Ausgabe zu erhalten:

0 
1 
2 
Pointer handled: 
0 
1 
2 
null 

Aber was ich eigentlich ist:

0 
1 
2 
Reference: 010AF7E4 
Reference: 00000000 

Der automatische Typabzug scheint die Referenzimplementierung auszuwählen und den Typ auf someOtherClass* festzulegen, anstatt die Zeigerimplementierung auszuwählen. Ich bin 2012

+0

Sie benötigen einen [MCVE] –

+0

@PasserBy: Die bereitgestellten Code in sich geschlossen ist und reproduziert das Problem – Mat

+0

@Mat Es vollständig und überprüfbar ist, aber nicht minimal. Das hört sich ein wenig wählerisch an, aber ich denke, es hilft, die Zeit für alle Beteiligten zu reduzieren. Der Autor das Problem auf seine minimal reduzieren, ist schneller als andere –

Antwort

0

In logger& operator << (const _T& rBar) Typ Visual Studio T kann ein Zeigertyp sein, also um richtig diese Vorlage zu arbeiten braucht einige Einschränkung:

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

Dies ist erforderlich, weil Wenn Vorlagen instanziiert werden, wird die const _T & pBar Variante mit _T = someOtherClass * angeboten, da die in diesem Fall erforderliche Konvertierungssequenz nur eine Referenzbindung enthält, die als Identitätskonvertierung betrachtet wird, während const _T* pBar Variante mit _T = someOtherClass wird eine Kopierinitialisierung beinhalten.

+2

Sie sollten hinzufügen, warum konstante Referenz über const Zeiger in der Überladungsauflösung –

+0

Danke für die Erklärung und mögliche Lösung gerichtet.Leider verfügt VS2012 nicht über eine vollständige C++ 11-Implementierung, und ich kann keinen Standardtyp für eine Funktionsdefinition angeben (nur für Vorlagenklassen). Gibt es eine andere Möglichkeit, eine Einschränkung hinzuzufügen? –

+1

Sie können eine weitere Überladung hinzufügen - das akzeptiert T * - sollte es über Referenz-Überladung ausgewählt werden, wenn der Zeiger nicht const ist –

0

Hier sind ein paar Änderungen und Anmerkungen, die helfen können, wenn diese Protokollierungsklasse wächst und reifer wird.

Ich habe versucht:

a) lösen, das Ausgangsproblem der falschen Typ Abzug.

b) entkoppeln der Logger von dem, was protokolliert wird (sonst Logger über die gesamte Anwendung wissen muss, und alle Bibliotheken).

c) einen Mechanismus bereitstellen, der die Protokollierung eines beliebigen Typs problemlos zulässt, selbst wenn er von einer Bibliothek eines Drittanbieters bereitgestellt wird.

#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; 
} 

erwartete Ausgabe:

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