gcc 当传递long时,int32_t或int64_t参数之间的模糊符重载

iq0todco  于 2023-10-19  发布在  其他
关注(0)|答案(4)|浏览(142)

**注意:**这与Determine number of bits in integral type at compile time非常相似,但这是一个非常简化的版本,所有内容都包含在一个.cpp

问题在于,

msg(int32_t);
 msg(int64_t);

一个电话,

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

在MSVC上编译。谁能解释一下为什么这在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
pbgvytdp

pbgvytdp1#

格雷格一针见血:int32_tint64_t是typedef,可能是也可能不是long。如果两者都不是long的typedef,重载解析可能失败。long->int32_tlong->int64_t均具有秩=提升(表12,13.3.3.1.2)

zbdgwd5y

zbdgwd5y2#

代码是否可以编译是由实现定义的。没有类型int32_tint64_t;这些只是现有整型的typedef。如果类型恰好是一个已经重载的类型(intlonglong long),这几乎是肯定的,那么你有同一个函数的多个定义。如果它们在同一个翻译单元中,则是编译时错误,需要进行诊断。如果它们在不同的翻译单元中,这是未定义的行为,但我想大多数实现也会生成错误。
在您的情况下,最好的解决方案可能是将msg作为模板,并将类型的名称作为参数传递。

vltsax25

vltsax253#

| 免责声明|
| --|
| 这个解决方案最初是由作者在他们的问题中发布的。|
解决方案是提供一个函数,成功地将intlonglong longMap到适当的int32_tint64_t。这可以在运行时通过if (sizeof(int)==sizeof(int32_t))类型语句简单地完成,但编译时解决方案更可取。通过使用boost::enable_if可以获得编译时解决方案。
以下代码适用于MSVC 10和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;
}
ogsagwnx

ogsagwnx4#

正如在其他答案中提到的,模板参数推导在这种情况下可以提供帮助,但它不足以消除<cstdint>中定义的所有可能的typedef之间的歧义。

#include <iostream>
#include <cstdint>
#include <concepts>
#include <string_view>
#include <format>

template< std::integral I >
constexpr auto name_of(I v)
    -> std::string_view
{
    if constexpr ( std::same_as<I, char> )
        return "char";
    else if constexpr ( std::unsigned_integral<I> )
        if constexpr ( sizeof v < 2 )
            return "uint8_t";
        else if constexpr ( sizeof v < 4 )
            return "uint16_t";
        else if constexpr ( sizeof v < 8 )
            return "uint32_t";
        else
            return "uint64_t";  
    else
        if constexpr ( sizeof v < 2 )
            return "int8_t";
        else if constexpr ( sizeof v < 4 )
            return "int16_t";
        else if constexpr ( sizeof v < 8 )
            return "int32_t";
        else
            return "int64_t";  
}

template< std::integral I >
void msg2(I v)
{
    std::cout << std::format("{:8}{:3}  ", name_of(v), sizeof v);
    
    if constexpr ( std::same_as<I, char> )
        std::cout << '\'' << v << "\'\n";
    else if constexpr ( sizeof(I) == 1 )
        std::cout << static_cast<int>(v) << '\n';
    else
        std::cout << v << '\n';
}

int main()
{
    msg2(-5);
    msg2(-6LL);
    msg2(-7LL);
    msg2(5U);
    msg2(6UL);
    msg2(7ULL);
  
    msg2(int8_t{42});
    msg2((signed char)'a');   // <-- Indistinguishable from int8_t
    msg2('b');
    msg2((unsigned char)'c'); // <-- Same as uint8_t
}

Demo

相关问题