2012-05-14 14 views
6

Hinweis: Dies ist sehr ähnlich zu Determine number of bits in integral type at compile time, jedoch ist dies eine viel vereinfachte Version, alle enthalten in einem einzigen .cppMehrdeutige Überladung von Funktionen wie `msg (long)` mit Kandidaten `msg (int32_t)` und `msg (int64_t)`

bearbeiten: eine Lösung - obwohl eine korrekte Erklärung gegeben wurde (und akzeptiert) ich einen Weg gefunden, das Problem generell zu lösen.

Problem

Das Problem ist mit Funktionen wie

msg(int32_t); 
msg(int64_t); 

ein Anruf wie

long long myLong = 6; 
msg(myLong); // Won't compile on gcc (4.6.3), call is ambiguous 

Dieses auf MSVC kompiliert. Kann jemand erklären, warum dies auf gcc fehlschlägt (ich gehe davon aus, dass es wahrscheinlich damit zu tun hat, dass gcc normalerweise strikt normenkonform ist) und ein Beispiel dafür, wie man den gleichen Effekt erzielt?

#include <iostream> 
#include <stdint.h> 

#include <boost/integer.hpp> 

using namespace std; 

void msg(int v) { cout << "int: " << sizeof(int) << ' ' << v << '\n'; } 
void msg(long v) { cout << "long: " << sizeof(long) << ' ' << v << '\n'; } 
void msg(long long v) { cout << "long long: " << sizeof(long long) << ' ' << v << '\n'; } 

void msg2(int32_t v) { cout << "int32_t: " << sizeof(int32_t) << ' ' << v << '\n'; } 
void msg2(int64_t v) { cout << "int64_t: " << sizeof(int64_t) << ' ' << v << '\n'; } 
void msg2(uint32_t v) { cout << "uint32_t: " << sizeof(uint32_t) << ' ' << v << '\n'; } 
void msg2(uint64_t v) { cout << "uint64_t: " << sizeof(uint64_t) << ' ' << v << '\n'; } 


int main() 
{ 

    int myInt = -5; 
    long myLong = -6L; 
    long long myLongLong = -7LL; 

    unsigned int myUInt = 5; 
    unsigned int myULong = 6L; 
    unsigned long long myULongLong = 7LL; 

    msg(myInt); 
    msg(myLong); 
    msg(myLongLong); 

    msg2(myInt); 
    msg2(myLong);  // fails on gcc 4.6.3 (32 bit) 
    msg2(myLongLong); 

    msg2(myUInt); 
    msg2(myULong); // fails on gcc 4.6.3 (32 bit) 
    msg2(myULongLong); 

    return 0; 
} 

// Output from MSVC (and gcc if you omit lines that would be commented out) 
int: 4 5 
long: 4 6 
long long: 8 7 
int32_t: 4 -5 
int32_t: 4 -6 // omitted on gcc 
int64_t: 8 -7 
uint32_t: 4 5 
uint32_t: 4 6 // omitted on gcc 
uint64_t: 8 7 

Lösung

Die Lösung ist eine Funktion bereitstellen, die erfolgreich int abbildet, und longlong long an den entsprechenden int32_t oder int64_t. Dies kann zur Laufzeit über die Anweisungen des Typs if (sizeof(int)==sizeof(int32_t)) trivial erfolgen, eine Kompilierungslösung ist jedoch vorzuziehen. Eine kompilierungsfähige Lösung ist über die Verwendung von boost::enable_if verfügbar.

Folgendes funktioniert auf MSVC10 und gcc 4.6.3. Die Lösung könnte weiter verbessert werden, indem für nicht-integrale Typen deaktiviert wird, aber das liegt außerhalb des Bereichs dieses Problems.

#include <iostream> 
#include <stdint.h> 

#include <boost/integer.hpp> 
#include <boost/utility/enable_if.hpp> 
#include <boost/type_traits/is_signed.hpp> 
#include <boost/type_traits/is_unsigned.hpp> 

using namespace std; 

template <class InputT> 
typename boost::enable_if_c<sizeof(InputT)==sizeof(int32_t) && boost::is_signed<InputT>::value, 
int32_t>::type ConvertIntegral(InputT z) { return static_cast<int32_t>(z); } 

template <class InputT> 
typename boost::enable_if_c<sizeof(InputT)==sizeof(int64_t) && boost::is_signed<InputT>::value, 
int64_t>::type ConvertIntegral(InputT z) { return static_cast<int64_t>(z); } 

template <class InputT> 
typename boost::enable_if_c<sizeof(InputT)==sizeof(uint32_t) && boost::is_unsigned<InputT>::value, 
uint32_t>::type ConvertIntegral(InputT z) { return static_cast<uint32_t>(z); } 

template <class InputT> 
typename boost::enable_if_c<sizeof(InputT)==sizeof(uint64_t) && boost::is_unsigned<InputT>::value, 
uint64_t>::type ConvertIntegral(InputT z) { return static_cast<uint64_t>(z); } 

void msg(int v) { cout << "int: " << sizeof(int) << ' ' << v << '\n'; } 
void msg(long v) { cout << "long: " << sizeof(long) << ' ' << v << '\n'; } 
void msg(long long v) { cout << "long long: " << sizeof(long long) << ' ' << v << '\n'; } 


void msg2(int32_t v) { cout << "int32_t: " << sizeof(int32_t) << ' ' << v << '\n'; } 
void msg2(int64_t v) { cout << "int64_t: " << sizeof(int64_t) << ' ' << v << '\n'; } 
void msg2(uint32_t v) { cout << "uint32_t: " << sizeof(uint32_t) << ' ' << v << '\n'; } 
void msg2(uint64_t v) { cout << "uint64_t: " << sizeof(uint64_t) << ' ' << v << '\n'; } 

int main() 
{ 

    int myInt = -5; 
    long myLong = -6L; 
    long long myLongLong = -7LL; 

    unsigned int myUInt = 5; 
    unsigned int myULong = 6L; 
    unsigned long long myULongLong = 7LL; 

    msg(myInt); 
    msg(myLong); 
    msg(myLongLong); 

    msg2(ConvertIntegral(myInt)); 
    msg2(ConvertIntegral(myLong)); 
    msg2(ConvertIntegral(myLongLong)); 

    msg2(ConvertIntegral(myUInt)); 
    msg2(ConvertIntegral(myULong)); 
    msg2(ConvertIntegral(myULongLong)); 

    return 0; 
} 
+0

MSVC hat 'typedef _Longlong int64_t' und gcc hat' typedef long long int int64_t', so glaube ich, die Art ist die gleiche in beiden Fällen . Auf jeden Fall ist es der Aufruf mit _long_, der das Problem ist ... – Zero

Antwort

2

Greg trifft den Nagel auf den Kopf: int32_t und int64_t sind typedefs, die long sein können oder auch nicht. Wenn keine der beiden Typen typedef für long ist, kann die Überladungsauflösung fehlschlagen. Beide long->int32_t und long->int64_t haben Rang = Promotion (Tabelle 12, 13.3.3.1.2)

3

Ob der Code kompiliert wird oder nicht, ist die Implementierung definiert. Es gibt keinen Typ int32_t noch int64_t; Dies sind einfach typedefs zu einem existierenden integralen Typ. Wenn der Typ zufällig bereits überladen ist (int, long oder long long), was fast sicher der Fall ist, dann haben Sie mehrere Definitionen derselben Funktion. Wenn sie sich in derselben Übersetzungseinheit befinden, handelt es sich um einen Kompilierzeitfehler, der eine Diagnose erfordert. Wenn sie sich in verschiedenen Übersetzungseinheiten befinden, ist das Verhalten nicht definiert, aber ich stelle mir vor, dass die meisten Implementierungen ebenfalls einen Fehler erzeugen.

In Ihrem Fall ist wahrscheinlich die beste Lösung, msg eine Vorlage zu machen, und übergeben Sie den Namen des Typs als Argument.

+1

Wie kann es der Fall sein, dass 'int32_t' und' int64_t' vom gleichen Typ sind? Sollte man nicht zweimal die Bits des anderen haben (wie in der Ausgabe des Testcodes gezeigt)?Auf jeden Fall hat gcc 'typedef int int32_t' und' typedef long long int int64_t', so dass sie in diesem speziellen Fall nicht identisch sind, selbst wenn sie in einer anderen Architektur identisch sein könnten. – Zero

+4

Es gibt keine Mehrfachdefinitionen der gleichen Funktionen. Dies könnte nur passieren, wenn "int32_t" dasselbe ist wie "int64_t" - was unmöglich ist. Das eigentliche Problem hier ist, dass in Gcc auf 32bit-Plattformen weder 'int32_t' noch' int64_t' typedef von 'long' ist (das erste ist nur' int', das zweite ist 'long long'). Wenn Sie versuchen, msg (long) aufzurufen, ist dies daher nicht eindeutig – Greg

+1

@Greg Ja. Ich hatte die Tatsache übersehen, dass die Funktionen unterschiedliche Namen hatten, was natürlich alles veränderte. (Oder nicht, weil, wie Sie darauf hinweisen, der genaue Typ von 'int32_t' und' int64_t' implementiert ist. Und da es drei Aufrufe von 'msg2' gibt, mit drei verschiedenen Typen, wird mindestens einer nicht genau sein Übereinstimmung und abhängig von den tatsächlichen typedefs, möglicherweise mehrdeutig. –