Вопрос по gcc, c++ – Неоднозначная перегрузка функций типа `msg (long)` с кандидатами `msg (int32_t)` и `msg (int64_t)`

7

Note: Это очень похоже наОпределить количество бит в целочисленном типе во время компиляцииОднако, это очень упрощенная версия, все содержится в одном.cpp

Edit: Добавлено решение - хотя и правильноеexplanation был дан (и принят), я нашел способ решить проблему в общем.

Problem

Проблема с такими функциями, как

<code> msg(int32_t);
 msg(int64_t);
</code>

звонок как

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

Это компилируется на MSVC. Может ли кто-нибудь дать объяснение, почему это не работает на gcc (я предполагаю, что это, вероятно, связано с тем, что gcc обычно строго соответствует стандартам), и пример того, как правильно достичь того же эффекта?

<code>#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
</code>
Solution

Решение состоит в том, чтобы обеспечить функцию, которая успешно отображаетint, long а такжеlong long к соответствующемуint32_t или жеint64_t, Это можно сделать тривиально во время выполнения с помощьюif (sizeof(int)==sizeof(int32_t)) операторы типа, но решение во время компиляции предпочтительнее. Решение времени компиляции доступно через использованиеboost::enable_if.

Следующие работы на MSVC10 и gcc 4.6.3. Решение может быть дополнительно улучшено путем отключения для нецелых типов, но это выходит за рамки этой проблемы.

<code>#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;
}
</code>
MSVC имеетtypedef _Longlong int64_t и GCC имеетtypedef long long int int64_t, так что я думаю, что тип одинаков в обоих случаях. Во всяком случае, это вызов с использованиемlong это проблема... Zero

Ваш Ответ

2   ответа
3

определяется реализацией. Нет типаint32_t ниint64_t; это просто typedef для существующего целочисленного типа. Если тип оказывается перегруженным (int, long или жеlong long), что почти наверняка так, тогда у вас есть несколько определений одной и той же функции. Если они находятся в одном модуле перевода, это ошибка времени компиляции, требующая диагностики. Если они находятся в разных единицах перевода, это неопределенное поведение, но я предполагаю, что большинство реализаций также будет выдавать ошибку.

В вашем случае, лучшее решение, вероятно, сделатьmsg шаблон и передайте имя типа в качестве аргумента.

Не существует нескольких определений одних и тех же функций. Это может произойти, только еслиint32_t такой же какint64_t - что невозможно. Проблема в том, что в gcc на 32-битных платформах ниint32_t ниint64_t это typedef изlong (сначала простоintвторойlong long). Вот почему, когда вы пытаетесь вызвать msg (long), это неоднозначно
Как это может быть так, чтоint32_t а такжеint64_t одного типа? Разве у одного не должно быть двух битов другого (как показано в выходных данных тестового кода)? В любом случае, GCC имеетtypedef int int32_t а такжеtypedef long long int int64_tТаким образом, они не одинаковы в данном конкретном случае, даже если они могут быть одинаковыми на некоторых других архитектурах. Zero
@ Грег Да. Я упустил тот факт, что функции имели разные имена, что, конечно, все меняет. (Или нет, потому что, как вы указываете, точный типint32_t а такжеint64_t определяется реализация. И так как есть три звонкаmsg2с тремя различными типами, по крайней мере один из них не будет точным соответствием, и в зависимости от фактических typedefs может быть неоднозначным.
3

int32_t а такжеint64_t являются typedefs, которые могут или не могут бытьlong, Если ни один не является typedef дляlong, разрешение перегрузки может не получиться. И то и другоеlong->int32_t а такжеlong->int64_t иметь ранг = повышение (таблица 12, 13.3.3.1.2)

Похожие вопросы