2012-05-14 10 views
6

Uwaga: Jest to bardzo podobne do Determine number of bits in integral type at compile time, jednak jest to wersja znacznie uproszczona, wszystkie zawarte w jednym .cppNiejednoznaczne przeciążanie funkcji takich jak `msg (długie)` z kandydatami `msg (int32_t)` i `msg (int64_t)`

Edytuj: Dodano rozwiązanie - choć poprawna wyjaśnienie dano (i zaakceptowane) znalazłem sposób, aby rozwiązać problem ogólnie.

Problem

Problem jest z funkcjami jak

msg(int32_t); 
msg(int64_t); 

wywołanie jak

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

To kompiluje na MSVC. Czy ktokolwiek może podać wyjaśnienie, dlaczego to się nie powiedzie na gcc (zakładam, że prawdopodobnie ma to związek z faktem, że gcc jest zwykle ściśle zgodny ze standardami) i przykładem, jak poprawnie osiągnąć ten sam efekt?

#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 

Rozwiązanie

Roztwór zapewnić funkcję skutecznie mapuje int, long i long long do odpowiedniego int32_t lub int64_t. Można to zrobić trywialnie w czasie wykonywania za pomocą instrukcji typu if (sizeof(int)==sizeof(int32_t)), ale preferowane jest rozwiązanie kompilujące. Rozwiązanie oparte na kompilacji jest dostępne za pośrednictwem aplikacji boost::enable_if.

Następujące prace na MSVC10 i gcc 4.6.3. Rozwiązanie można jeszcze bardziej poprawić, wyłączając dla typów nie-integralnych, ale wykracza to poza zakres tego problemu.

#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 ma 'typedef _Longlong int64_t' i gcc ma' typedef long long int int64_t', więc myślę, że typ jest taki sam w obu przypadkach . W każdym razie jest to połączenie używające _long_, które jest problemem ... – Zero

Odpowiedz

2

Greg uderza w gwóźdź w głowę: int32_t i int64_t są typedefs, które mogą ale nie muszą być long. Jeśli nie jest to typedef dla long, rozdzielczość przeciążania może się nie udać. Zarówno long->int32_t i long->int64_t mieć Rank = Promotion (tabela 12, 13.3.3.1.2)

3

Określa, czy kod zostanie skompilowany, czy nie. Nie ma typu int32_t ani int64_t; są one po prostu typedefs do istniejącego typu integralnego. Jeśli typ jest taki, który jest już przeciążony (int, long lub long long), co prawie na pewno ma miejsce, to masz wiele definicji tej samej funkcji. Jeśli znajdują się w tej samej jednostce tłumaczeniowej, jest to błąd czasu kompilacji, wymagający diagnostyki. Jeśli są w różnych jednostkach tłumaczeniowych, jest to niezdefiniowane zachowanie, ale wyobrażam sobie, że większość implementacji również wygeneruje błąd.

W twoim przypadku najlepszym rozwiązaniem jest prawdopodobnie uczynienie msg szablonu i podanie nazwy typu jako argumentu.

+1

Jak to możliwe, że 'int32_t' i' int64_t' są tego samego typu? Czy nie powinien mieć dwa razy bity drugiego (jak pokazano na wyjściu kodu testowego)?W każdym razie, gcc ma 'typedef int int32_t' i' typedef long long int int64_t', więc nie są one takie same w tym konkretnym przypadku, nawet jeśli mogą być takie same w innej architekturze. – Zero

+4

Nie ma wielu definicji tych samych funkcji. Może się to zdarzyć tylko wtedy, gdy 'int32_t' jest identyczne z' int64_t' - co jest niemożliwe. Rzeczywistym problemem jest to, że w gcc na platformach 32-bitowych ani 'int32_t' ani' int64_t' nie jest typu 'long' (pierwszy to po prostu' int', drugi to 'long long'). To dlatego, gdy próbujesz wywołać msg (długie), jest niejednoznaczne – Greg

+1

@Greg Tak. Brakowało mi tego, że funkcje mają różne nazwy, co oczywiście zmienia wszystko. (Lub nie, ponieważ jak wskazujesz, dokładny typ 'int32_t' i' int64_t' jest zdefiniowany przez implementację, a ponieważ istnieją trzy wywołania 'msg2', z trzema różnymi typami, przynajmniej jeden nie będzie dokładny Dopasowanie iw zależności od rzeczywistych typów może być niejednoznaczne. –

Powiązane problemy