nixp.ru v3.0

16 января 2017,
понедельник,
15:59:02 MSK

DevOps с компанией «Флант»
Аватар пользователя rgo
rgo написал 15 августа 2012 года в 20:54 (3039 просмотров) Ведет себя неопределенно; открыл 61 тему в форуме, оставил 1602 комментария на сайте.

Тут ещё бывает кто-нибудь, кто знаком с C++?

Вопрос тут такой возник… Различны ли size_t и unsigned для механизма перегрузки функций в C++? При условии, что sizeof(size_t) == sizeof(unsigned) и size_t тоже беззнаковый. Короче, при условии, что size_t объявлен typedef’ом как unsigned int.

Если вопрос не совсем понятен, я могу пример привести, допустим есть функция int foo(unsigned); Допустимо ли перегрузить её таким образом: int foo(size_t)?

Вопрос может быть достаточно ламерский, но мне просто лень искать какую-нибудь документацию на стандарт C++. Тем более что стандартов много, а какой именно мне нужен — я не знаю, я с C++ завязал лет десять назад, и сегодня совершенно не в курсе.

arhimedoffs

Я так понял речь идет о чем-то таком

//————————————————————
typedef int some;

int func(int i) {

return i*2;

};

int func(some i) {

return int(i)*2;

};

int main(int argc, char *argv[]) {

int i = 1;

some j = 1;

int res = func(i);

int res2 = func(j);

return 0;

}

//————————————————————

На попытку компиляции g++ ответил следующим образом

$ g++ main.cpp 
main.cpp: In function ‘int func(some)’:
main.cpp:7:5: error: redefinition of ‘int func(some)’
main.cpp:3:5: error: ‘int func(int)’ previously defined here 


т.е. для механизма перегрузки это одинаковые типы, а значит перегрузить не получиться.

P.S. Из личного опыта скажу, что в подобных «непонятках» часто помогают коротенькие тестовые примерчики. Не всегда то что написано в стандартах воспринимается по прочтению.

arhimedoffs

Копнув немного глубже нашел www.learncpp.com/cpp-tutorial/76-function-overloading/ описано весьма неплохо механизм выбора функции при перегрузке.

Ну и на любимом www.cplusplus.com нашел хорошее объяснение почему через typedef не работает:

typedef does not create different types. It only creates synonyms of existing types. ( www.cplusplus.com/doc/tutorial/other_data_types/ )

 

 

rgo

Спасибо за исследование. =)

defender

Тем не менее:

$ cat test.cpp:

#include <iostream>
using namespace std;
void foo(unsigned int f) { cout << "foo(unsigned int): " << f << endl; }
void foo(size_t f) { cout << "foo(size_t): " << f << endl; }
int main(int argc, char* argv[]) { unsigned int uint = 5; size_t ssize = 10;
foo(uint); foo(ssize);
return 0; }


$ g++  -o prog test.cpp

$ ./prog

foo(unsigned int): 5
foo(size_t): 10


 

PS

Это если вопрос про size_t. Он никак не unsigned int.

rgo

Но как бы там ни было, главный вывод в том, что «typedef does not create different types». Достаточно очевидная вещь, если подумать, но меня видимо lisp’овский deftype с толку сбил. Он немного иначе себя ведёт, но в C (с которым я постоянно сталкиваюсь) разница не заметна. Сказывается лишь в C++.

arhimedoffs

На утверждение что «Это если вопрос про size_t. Он никак не unsigned int.» можно утверждать, что это зависит от платформы. Если скомпилировать Ваш же пример, но как

g++ -fpermissive -D __SIZE_TYPE__="unsigned" main.cpp 


То получим ту же ошибку переопределения

<command-line>:0:0: warning: "__SIZE_TYPE__" redefined [enabled by default]
main.cpp:1:0: note: this is the location of the previous definition
main.cpp: In function ‘void foo(size_t)’:
main.cpp:10:6: error: redefinition of ‘void foo(size_t)’
main.cpp:5:6: error: ‘void foo(unsigned int)’ previously defined here 


Так что может где size_t идет как unsigned int, это еще неизвестно.

 

defender

Ну это в ембеде где-нить может можно делать. На обычных системах 4гига файлик — не очень приятно. В линухе size_t объявлен как long в ядреном коде  (а выстрелить в ногу можно и другими, не менее прекрасными методами :D )

arhimedoffs

Что-то я не совсем понял Ваш ответ. Если Вы про то, что я особо извратился — отнюдь, просто для некоторых аппаратных платформ это может быть может быть по-умолчанию. А вот size_t объявлен не в ядреном коде, где лишь используется, а в самом GCC, а там уж что в нем прописано — как повезет. Я думаю что не зря size_t определяется не напрямую, а через дополнительные макросы со значением по-умолчанию, в файле gcc/c-family/c-common.c

builtin_define_with_value ("__SIZE_TYPE__", SIZE_TYPE, 0);


В тех же исходниках gcc-4.7 есть файл gcc/stor-layout.c, что предусматривает всевозможные варианты

/* Get sizetypes precision from the SIZE_TYPE target macro.  */
  if (strcmp (SIZE_TYPE, "unsigned int") == 0)
    precision = INT_TYPE_SIZE;
  else if (strcmp (SIZE_TYPE, "long unsigned int") == 0)
    precision = LONG_TYPE_SIZE;
  else if (strcmp (SIZE_TYPE, "long long unsigned int") == 0)
    precision = LONG_LONG_TYPE_SIZE;
  else if (strcmp (SIZE_TYPE, "short unsigned int") == 0)
    precision = SHORT_TYPE_SIZE;
  else
    gcc_unreachable (); 


Хотя просмотрев все конфиги для разных архитектур получилось

  • для 23 — #define SIZE_TYPE «long unsigned int»
  • для 13 — вариации на тему #define SIZE_TYPE (TARGET_LP64? «long unsigned int»: «unsigned int»)
  • для 40 — #define SIZE_TYPE «unsigned int»

Очень много архитектур экзотичных, но вот значимые

i386/x86-64.h:#define SIZE_TYPE (TARGET_LP64 ? "long unsigned int" : "unsigned int")
i386/freebsd.h:#define SIZE_TYPE    (TARGET_64BIT ? "long unsigned int" : "unsigned int")
i386/gnu-user.h:#define SIZE_TYPE "unsigned int"
mips/mips.h:#define SIZE_TYPE (POINTER_SIZE == 64 ? "long unsigned int" : "unsigned int")


Собственно мы далеко отошли от темы. При оригинальной постановке вопроса однозначный ответ «Нет».

А так, то теоретически можно остаться с простреленой ногой даже не имея пистолета :)

 

defender

Тут  я понимаю следующее. Ядро собрано с таким size_t, который является стандартным для когнфига GCC (ну предположим что собираем для платформы на которой работаем и не играем в темные игры). ТОгда не очень весело компилить свои бинари, переопределив size_t. Поскольку можно слегка нарваться при выполнении системного вызова.

arhimedoffs

Да не говорю я что его нужно обязательно переопределять. Я просто категорически не согласен с Вашим высказыванием:

«В линухе size_t объявлен как long в ядреном коде»

Он не обязательно long и задается не в коде ядра, а скорее уж в libc, а именно в stddef.h, который у меня содержит код

#ifndef __SIZE_TYPE__
#define __SIZE_TYPE__ long unsigned int
#endif
#if !(defined (__GNUG__) && defined (size_t))
typedef __SIZE_TYPE__ size_t;
#ifdef __BEOS__
typedef long ssize_t;
#endif /* __BEOS__ */
#endif /* !(defined (__GNUG__) && defined (size_t)) */


А вот __SIZE_TYPE__ по-умолчанию как раз и есть SIZE_TYPE, который далеко не всегда unsigned long int, что было изложено выше. И это все без «тёмных игр» и чего либо подобного.

Вот с чем соглашусь, так это то, что особо не стоит менять и переопределять стандартные вещи, если это не является необходимостью.

arhimedoffs

Не поленился и поставил chroot с i486 Debian testing. Итог

#include <iostream>
using namespace std;
void foo(unsigned int f) { cout << "foo(unsigned int): " << f << endl; cout << sizeof(f) << endl; }
void foo(size_t f) { cout << "foo(size_t): " << f << endl; cout << sizeof(f) << endl; }
int main(int argc, char* argv[]) { unsigned int uint = 5; size_t ssize = 10;
foo(uint); foo(ssize);
return 0; }


g++-4.7 test.cpp

test.cpp: In function 'void foo(size_t)':
test.cpp:10:6: error: redefinition of 'void foo(size_t)'
test.cpp:5:6: error: 'void foo(unsigned int)' previously defined here 


и результат sizeof(size_t) — 4.

rgo

size_t — это для измерения буферов в оперативной памяти и всегда работает такое: sizeof(size_t) <= sizeof(void*). На 32-х битной платформе больше 32-бит size_t не будет. А для файликов больше 4Gb libc объявляет тип off_t. Который, кстати, тоже может быть 32-бита (и, вероятно, он всегда 32-бита на 32-х битной платформе), но чтобы исправить это, существует ещё off64_t.

ecobeingecobeing.ru
Экология и вегетарианство на благо всем живым существам Планеты.