2012-05-14 8 views
6

注:これは、単一.cpp`msg(int32_t)`と `msg(int64_t)`の候補を持つ `msg(long)`のような関数のあいまいなオーバーロード

編集に:解決策を追加しました - 正しい説明が与えられた(と受理)されたが、私は一般的に問題を解決する方法を発見しました。

問題

問題が

msg(int32_t); 
msg(int64_t); 

のような機能を持つ。これはMSVCでコンパイル

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

ような呼び出しです。誰もgccでこれがなぜ失敗するのかについての説明を誰でも提供できます(実際にはgccが通常厳密に標準に準拠していると想定しています)。同じ効果を正しく達成する方法の例は?

#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 

ソリューション

ソリューションが正常に適切なint32_tまたはint64_tintlonglong longをマップする機能を提供しています。これは、実行時にif (sizeof(int)==sizeof(int32_t))タイプのステートメントを使用して簡単に行うことができますが、コンパイル時の解決策が推奨されます。コンパイル時の解決策は、boost::enable_ifを使用して入手できます。

以下は、MSVC10およびgcc 4.6.3で動作します。 非整数型を無効にすることでソリューションをさらに強化できますが、これはこの問題の範囲外です。

#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にはtypedef _Longlong int64_tがあり、gccにはtypedef long long int int64_tがあるので、両方のケースで型が同じであると思います。とにかく、それは_long_を使った呼び出しです。問題は... – Zero

答えて

2

グレッグが頭の釘を打つ:int32_tおよびint64_tは、longである場合とそうでない場合があるtypedefです。両方ともlongのtypedefでない場合、オーバーロードの解決に失敗する可能性があります。 long->int32_tlong->int64_tの両方ともランク=プロモーション(表12,13.3.3.1.2)

3

コードがコンパイルされるかどうかは、実装によって定義されます。タイプはint32_tでもint64_tでもありません。これらは単にtypedefの既存の整数型です。ほとんどの場合、すでにオーバーロードされているタイプ(intlongまたはlong long)が存在する場合、同じ機能の複数の定義があります。それらが同じ翻訳単位内にある場合、それはコンパイル時エラーであり、診断が必要です。それらが異なる翻訳単位にある場合、定義されていない動作ですが、ほとんどの実装でもエラーが発生すると思います。

場合によっては、msgをテンプレートにして、型の名前を引数として渡すことをお勧めします。

+1

'int32_t'と' int64_t'が同じ型の場合はどうでしょうか?テストコードの出力に示されているように、他のビットの2倍のビットを持つべきではありませんか?とにかく、gccは 'typedef int int32_t'と' typedef long long int int64_t'を持っているので、他のアーキテクチャでも同じであっても、この特定のケースでは同じではありません。 – Zero

+4

同じ機能の定義は複数ありません。これは 'int32_t'が' int64_t'と同じ場合にのみ起こります - これは不可能です。 ここで問題となるのは、32ビットプラットフォームのgccでは、 'int32_t'も' int64_t'も 'long'のtypedefではありません(最初は' int'、もう一つは 'long long'です)。これは、msg(long)を呼び出そうとすると曖昧です。なぜなら、 – Greg

+1

@Gregはい。私は、関数が異なる名前を持っていたという事実を忘れてしまいました。 (そうではなく、 'int32_t'と' int64_t'の正確な型が実装定義されているため、3つの異なる型を持つ 'msg2'を3回呼び出すので、少なくとも1つは正確ではありません一致し、実際のtypedefによってはあいまいな場合があります。 –

関連する問題