MIN и MAX в C

301

Где MINи MAXопределены в C, если вообще?

Каков наилучший способ реализовать их как можно более обобщенно и безопасно? (Расширения / встроенные компиляторы для основных компиляторов предпочтительнее.)

Мэтт Джойнер
источник

Ответы:

392

Где MINи MAXопределены в C, если вообще?

Это не так.

Каков наилучший способ их реализации, максимально общий и безопасный для типов (предпочтительны расширения / встроенные компиляторы для основных компиляторов).

Как функции. Я бы не стал использовать такие макросы #define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)), особенно если вы планируете развернуть свой код. Либо написать свое собственное, использовать что - то вроде стандарта fmaxили fmin, или исправить макрос с помощью TYPEOF GCC в (вы получите бонус типобезопасности тоже) в выражении НКИ заявления :

 #define max(a,b) \
   ({ __typeof__ (a) _a = (a); \
       __typeof__ (b) _b = (b); \
     _a > _b ? _a : _b; })

Все говорят: «О, я знаю о двойной оценке, это не проблема», и через несколько месяцев вы будете часами подряд отлаживать самые глупые проблемы.

Обратите внимание на использование __typeof__вместо typeof:

Если вы пишете заголовочный файл, который должен работать при включении в программы ISO C, пишите __typeof__вместо typeof.

Дэвид Титаренко
источник
68
Вы знаете, было бы очень удобно, если бы gcc имел предупреждение в виде: warning: expression with side-effects multiply evaluated by macroв месте использования ...
caf
23
@caf: не требует ли препроцессор более сложных знаний синтаксиса C?
сонлакс
3
После долгих попыток выяснить это, я не думаю, что в любом случае это можно сделать в VC ++, но лучше всего попытаться возиться с новым decltypeключевым словом MSVC ++ 2010 - но даже в этом случае Visual Studio не может выполнять составные операторы в макросах. (и decltypeв любом случае это C ++), то есть ({ ... })синтаксис GCC, так что я уверен, что в любом случае это невозможно. Я не смотрел на другие компиляторы по этой проблеме, извините Лютер: S
Дэвид Титаренко
7
@dreamlax Однажды я видел случай, когда кто-то пытался MAX(someUpperBound, someRandomFunction())ограничить случайное значение до некоторой верхней границы. Это была ужасная идея, но она также не работала, потому что MAXон использовал проблему двойной оценки, поэтому он получил случайное число, отличное от того, которое было первоначально оценено.
Зев Айзенберг,
8
@Soumen Например, если вы вызываете MIN(x++, y++)препроцессор, будет сгенерирован следующий код (((x++) < (y++)) ? (x++) : (y++)). Итак, xи yбудет увеличен в два раза.
Антонио
91

Он также предоставляется в версиях sys / param.h для GNU libc (Linux) и FreeBSD и имеет определение, предоставленное dreamlax.


На Debian:

$ uname -sr
Linux 2.6.11

$ cat /etc/debian_version
5.0.2

$ egrep 'MIN\(|MAX\(' /usr/include/sys/param.h
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))

$ head -n 2 /usr/include/sys/param.h | grep GNU
This file is part of the GNU C Library.

На FreeBSD:

$ uname -sr
FreeBSD 5.5-STABLE

$ egrep 'MIN\(|MAX\(' /usr/include/sys/param.h
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))

Исходные репозитории находятся здесь:

Mikel
источник
Я добавил определения из систем, к которым у меня есть доступ, в своем ответе выше (насколько я могу судить, поле комментария не принимает форматирование). Постараюсь найти ссылки на репозитории FreeBSD / Linux / glibc.
Микель
+1. Очень хорошо. Работает для openSUSE/Linux 3.1.0-1.2-desktop/ gcc version 4.6.2 (SUSE Linux) тоже. :) Плохо, что это не портативно.
Джек,
Работает на Cygwin тоже.
CMCDragonkai
1
Подождите минутку. Это не мешает двойной оценке, не так ли? : 3
user1857492
76

Есть std::minи std::maxв C ++, но AFAIK, в стандартной библиотеке C нет эквивалента. Вы можете определить их самостоятельно с помощью макросов, таких как

#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))

Но это вызывает проблемы, если вы пишете что-то вроде MAX(++a, ++b).

dan04
источник
10
зачем ставить слишком много скобок ??? Я нашел тест, где они сказали, что #define MIN(A, B) ((A < B) ? A : B)это не гибкий способ, почему ???
79
@Makouda: дополнительные скобки в макросах помогают избежать проблем с приоритетом операторов. Например, рассмотрим #define MULT(x, y) x * y. Затем MULT(a + b, a + b)расширяется до a + b * a + b, который анализирует как a + (b * a) + bиз-за приоритета. Это не то, что программист, вероятно, намеревался.
dan04
что не нужно, когда?: в любом случае имеет самый низкий приоритет
Вингер Сендон
1
@WingerSendon: это не так; оператор запятой делает.
Ден04
24

Избегайте нестандартных расширений компилятора и реализуйте его как полностью безопасный для типов макрос в чистом стандарте C (ISO 9899: 2011).

Решение

#define GENERIC_MAX(x, y) ((x) > (y) ? (x) : (y))

#define ENSURE_int(i)   _Generic((i), int:   (i))
#define ENSURE_float(f) _Generic((f), float: (f))


#define MAX(type, x, y) \
  (type)GENERIC_MAX(ENSURE_##type(x), ENSURE_##type(y))

использование

MAX(int, 2, 3)

объяснение

Макрос MAX создает другой макрос на основе typeпараметра. Этот макрос управления, если он реализован для данного типа, используется для проверки того, что оба параметра имеют правильный тип. Если typeне поддерживается, будет ошибка компилятора.

Если x или y имеет неправильный тип, в ENSURE_макросах будет ошибка компилятора . Можно добавить больше таких макросов, если поддерживается больше типов. Я предполагал, что будут использоваться только арифметические типы (целые числа, числа с плавающей запятой, указатели и т. Д.), А не структуры или массивы и т. Д.

Если все типы верны, будет вызван макрос GENERIC_MAX. Дополнительные скобки необходимы вокруг каждого параметра макроса, как обычная стандартная мера предосторожности при написании макросов Си.

Тогда есть обычные проблемы с неявным продвижением типов в C. ?:Оператор уравновешивает 2-й и 3-й операнд друг против друга. Например, результатом GENERIC_MAX(my_char1, my_char2)будет int. Чтобы макрос не мог выполнять такие потенциально опасные продвижения типа, использовался финальный тип, приведенный к предполагаемому типу.

обоснование

Мы хотим, чтобы оба параметра макроса были одного типа. Если один из них имеет другой тип, макрос больше не является типобезопасным, потому что оператор like ?:будет выдавать неявные продвижения типа. И поскольку это так, мы также всегда должны приводить конечный результат обратно к намеченному типу, как описано выше.

Макрос только с одним параметром мог бы быть написан намного проще. Но с 2 или более параметрами необходимо включить дополнительный параметр типа. Потому что что-то подобное, к сожалению, невозможно:

// this won't work
#define MAX(x, y)                                  \
  _Generic((x),                                    \
           int: GENERIC_MAX(x, ENSURE_int(y))      \
           float: GENERIC_MAX(x, ENSURE_float(y))  \
          )

Проблема состоит в том, что если вышеупомянутый макрос вызывается как MAX(1, 2)с двумя int, он все равно будет пытаться макрорасширить все возможные сценарии _Genericсписка ассоциаций. Таким образом, ENSURE_floatмакрос также будет расширен, хотя он не имеет отношения к int. А поскольку этот макрос намеренно содержит только floatтип, код не будет компилироваться.

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

Примеры

#include <stdio.h>

#define GENERIC_MAX(x, y) ((x) > (y) ? (x) : (y))

#define ENSURE_int(i)   _Generic((i), int:   (i))
#define ENSURE_float(f) _Generic((f), float: (f))


#define MAX(type, x, y) \
  (type)GENERIC_MAX(ENSURE_##type(x), ENSURE_##type(y))

int main (void)
{
  int    ia = 1,    ib = 2;
  float  fa = 3.0f, fb = 4.0f;
  double da = 5.0,  db = 6.0;

  printf("%d\n", MAX(int,   ia, ib)); // ok
  printf("%f\n", MAX(float, fa, fb)); // ok

//printf("%d\n", MAX(int,   ia, fa));  compiler error, one of the types is wrong
//printf("%f\n", MAX(float, fa, ib));  compiler error, one of the types is wrong
//printf("%f\n", MAX(double, fa, fb)); compiler error, the specified type is wrong
//printf("%f\n", MAX(float, da, db));  compiler error, one of the types is wrong

//printf("%d\n", MAX(unsigned int, ia, ib)); // wont get away with this either
//printf("%d\n", MAX(int32_t, ia, ib)); // wont get away with this either
  return 0;
}
Лундин
источник
GENERIC_MAXКстати, этот макрос - плохая идея, вам нужно только попытаться GENERIC_MAX(var++, 7)выяснить, почему :-) В настоящее время (особенно с сильно оптимизирующими / встроенными компиляторами), макросы в значительной степени следует относить только к простым формам. Подобные функции лучше как функции, а группы значений лучше как перечисления.
paxdiablo
21

Я не думаю, что это стандартизированные макросы. Уже есть стандартизированные функции для чисел с плавающей точкой fmaxи fminfmaxfдля чисел с плавающей запятой, и fmaxlдля длинных двойных чисел ).

Вы можете реализовать их как макросы, если вы знаете о побочных эффектах / двойной оценке.

#define MAX(a,b) ((a) > (b) ? a : b)
#define MIN(a,b) ((a) < (b) ? a : b)

В большинстве случаев вы можете оставить это на усмотрение компилятора, чтобы определить, что вы пытаетесь сделать, и оптимизировать его как можно лучше. Хотя это вызывает проблемы при использовании как MAX(i++, j++), я сомневаюсь, что когда-либо есть необходимость в проверке максимума приращенных значений за один раз. Сначала увеличьте, затем проверьте.

dreamlax
источник
Это должен быть предпочтительный ответ, поскольку в математической библиотеке явно есть функции min и max: cplusplus.com/reference/cmath/fmax
imranal
@imranal О чем именно ты говоришь? Код реализации этой библиотеки? Но этот код не раскрывается , то есть они не помещают его в интерфейс библиотеки, будучи потенциально небезопасными.
Антонио
@Antonio Я думаю, что вы используете неправильные определения «выставлены» и «интерфейс». Интерфейсом библиотеки ac являются внешние переменные, типы, макросы и объявления функций в заголовочном файле; fmin / fmax объявлены в заголовочном файле, поэтому они, как говорят, выставлены. Я не уверен, что вы называете небезопасным, хотя.
рациональный
21

Это поздний ответ из-за довольно недавнего развития событий. Поскольку OP принял ответ, основанный на непереносимом расширении GCC (и clang) typeof- или __typeof__на «чистом» ISO C - для gcc-4.9 доступно лучшее решение .

#define max(x,y) ( \
    { __auto_type __x = (x); __auto_type __y = (y); \
      __x > __y ? __x : __y; })

Очевидное преимущество этого расширения состоит в том, что каждый макро-аргумент раскрывается только один раз, в отличие от __typeof__решения.

__auto_typeэто ограниченная форма C ++ 11 auto. Он не может (или не должен?) Использоваться в коде C ++, хотя нет веской причины не использовать возможности превосходного вывода типов autoпри использовании C ++ 11.

Тем не менее, я предполагаю, что нет никаких проблем с использованием этого синтаксиса, когда макрос включен в extern "C" { ... }область действия; например, из заголовка C. AFAIK, это расширение не нашло свой путь

Бретт Хейл
источник
Связанный с комментарием Бретта Хейла , clangначал поддерживать __auto_typeоколо 2016 года (см. Патч ).
Ларс
Престижность признания проблемы макросъемки , но я по- прежнему постулировать , что функция, вероятно , будет лучше :-)
paxdiablo
@paxdiablo - согласен, хотя у вопроса есть c-preprocessorтег. Функция не гарантируется встроенной даже с указанным ключевым словом, если только не используется что-то вроде __always_inline__атрибута gcc .
Бретт Хейл
11

Я написал эту версию, которая работает для MSVC, GCC, C и C ++.

#if defined(__cplusplus) && !defined(__GNUC__)
#   include <algorithm>
#   define MIN std::min
#   define MAX std::max
//#   define TMIN(T, a, b) std::min<T>(a, b)
//#   define TMAX(T, a, b) std::max<T>(a, b)
#else
#       define _CHOOSE2(binoper, lexpr, lvar, rexpr, rvar) \
                ({ \
                        decltype(lexpr) lvar = (lexpr); \
                        decltype(rexpr) rvar = (rexpr); \
                        lvar binoper rvar ? lvar : rvar; \
                })
#       define _CHOOSE_VAR2(prefix, unique) prefix##unique
#       define _CHOOSE_VAR(prefix, unique) _CHOOSE_VAR2(prefix, unique)
#       define _CHOOSE(binoper, lexpr, rexpr) \
                _CHOOSE2( \
                        binoper, \
                        lexpr, _CHOOSE_VAR(_left, __COUNTER__), \
                        rexpr, _CHOOSE_VAR(_right, __COUNTER__) \
                )
#       define MIN(a, b) _CHOOSE(<, a, b)
#       define MAX(a, b) _CHOOSE(>, a, b)
#endif
Мэтт Джойнер
источник
1
Я проголосовал, но идентификаторы, начинающиеся со знака подчеркивания, следующего за заглавной буквой, зарезервированы.
Dreamlax
8

Если вам нужно мин / макс, чтобы избежать дорогостоящей ветки, вам не следует использовать троичный оператор, так как он скомпилируется до прыжка. Ссылка ниже описывает полезный метод для реализации функции min / max без ветвления.

http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax

CIB
источник
1
Если компилятор достаточно умен, он может избежать ветвления
Axel Gneiting
2
Если оптимизация включена, все современные компиляторы будут генерировать условный ход вместо ветки в большинстве случаев, поэтому нет смысла использовать подобные хаки.
Кшиштоф Косинский
2
Абсолютно верно, я понятия не имею, на что я тогда смотрел, это было давно. И gcc, и clang избегают ветвлений с -O, как на x86, так и на armv7a.
Cib
6

@David Titarenco прибил его здесь , но позвольте мне , по крайней мере в чистоте его немного , чтобы заставить это выглядеть красиво, и показать , как min() и max() вместе , чтобы скопировать и вставить здесь проще. :)

Обновление 25 апреля 2020 года. Я также добавил Раздел 3, чтобы показать, как это можно сделать с помощью шаблонов C ++, в качестве ценного сравнения для тех, кто изучает и C, и C ++, или переходя от одного к другому. Я приложил все усилия, чтобы быть тщательным, основанным на фактах и ​​правильным, чтобы сделать этот ответ каноническим справочником, к которому я могу возвращаться снова и снова, и я надеюсь, что вы найдете его таким же полезным, как и я.

1. Старый способ макроса C:

Этот метод обычно используется, его уважают те, кто знает, как правильно его использовать, «фактический» способ ведения дел, и его можно использовать при правильном использовании, но с ошибками (подумайте: побочный эффект двойной оценки ), если вы когда-либо передать выражения, включая присвоение переменной, для сравнения:

#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define MIN(a,b) ((a) < (b) ? (a) : (b))

2. Новый и улучшенный способ выражения выражения gcc :

Этот метод позволяет избежать вышеупомянутых «двойных оценок» побочных эффектов и ошибок и поэтому считается лучшим, более безопасным и «более современным» GCC C способом сделать это. Ожидайте, что он будет работать как с компиляторами gcc, так и clang, поскольку clang по своей конструкции совместим с gcc (см. Примечание clang внизу этого ответа).

НО: ДЕЙСТВИТЕЛЬНО остерегайтесь эффектов « затенения переменных », поскольку выражения операторов, по-видимому, встроены и, следовательно, НЕ имеют своей собственной области видимости локальной переменной!

#define max(a,b)             \
({                           \
    __typeof__ (a) _a = (a); \
    __typeof__ (b) _b = (b); \
    _a > _b ? _a : _b;       \
})

#define min(a,b)             \
({                           \
    __typeof__ (a) _a = (a); \
    __typeof__ (b) _b = (b); \
    _a < _b ? _a : _b;       \
})

Обратите внимание, что в выражениях операторов gcc последнее выражение в блоке кода - это то, что «возвращается» из выражения, как если бы оно было возвращено из функции. Документация GCC гласит это так:

Последним в составном операторе должно быть выражение, за которым следует точка с запятой; значение этого подвыражения служит значением всей конструкции. (Если вы используете какой-то другой вид оператора последним в фигурных скобках, конструкция имеет тип void и, следовательно, фактически не имеет значения.)

3. Шаблон C ++:

Примечание C ++: при использовании C ++ шаблоны, вероятно, рекомендуются вместо этого типа конструкции, но я лично не люблю шаблоны и, вероятно, буду использовать одну из вышеупомянутых конструкций в C ++, так как я часто использую и предпочитаю стили C во встроенном C ++.

Этот раздел добавлен 25 апреля 2020 года:

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

Вот какие основные шаблоны функции версии max()и min()может выглядеть в C ++:

template <typename T>
T max(T a, T b)
{
    return a > b ? a : b;
}

template <typename T>
T min(T a, T b)
{
    return a < b ? a : b;
}

Дополнительное чтение о шаблонах C ++ здесь: Wikipedia: Template (C ++) .

Тем не менее, оба max()и min()уже являются частью стандартной библиотеки C ++, в <algorithm>header ( #include <algorithm>). В стандартной библиотеке C ++ они определены немного иначе, чем у меня выше. Прототипы по умолчанию для std::max<>()и std::min<>(), например, в C ++ 14, если посмотреть их прототипы в ссылках cplusplus.com чуть выше:

template <class T> 
constexpr const T& max(const T& a, const T& b);

template <class T> 
constexpr const T& min(const T& a, const T& b);

Обратите внимание, что ключевое слово typenameявляется псевдонимом class(поэтому их использование идентично, говорите ли вы <typename T>или <class T>), поскольку после изобретения шаблонов C ++ позже было признано, что тип шаблона может быть обычным типом ( int, floatи т. Д.), А не только тип класса.

Здесь вы можете видеть, что оба типа ввода, а также тип возвращаемого значения const T&, что означает «постоянная ссылка на тип T». Это означает, что входные параметры и возвращаемое значение передаются по ссылке, а не по значению . Это похоже на прохождение указателями и более эффективно для больших типов, таких как объекты классов. constexprЧасть функции модифицирует сама функция и указывает , что функция должна быть способна оценивается во время компиляции (по крайней мере , если предусмотренных constexprвходных параметров), но если она не может быть оценена во время компиляции, то он по умолчанию обратно в оценка во время выполнения, как и любая другая нормальная функция.

Аспект времени компиляции constexprфункции C ++ делает ее своего рода C-макро-подобной, в том случае, если для constexprфункции возможна оценка во время компиляции , это будет сделано во время компиляции, так же, как могла бы быть подстановка MIN()или MAX()быть полностью оценены во время компиляции в C или C ++ тоже. Дополнительные ссылки на эту информацию шаблона C ++ см. Ниже.

Ссылки:

  1. https://gcc.gnu.org/onlinedocs/gcc/Typeof.html#Typeof
  2. https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html#Statement-Exprs
  3. MIN и MAX в C
  4. Дополнительные ссылки на шаблоны C ++ добавлены в апреле 2020 года:
    1. ***** Википедия: Шаблон (C ++) <- БОЛЬШАЯ дополнительная информация о шаблонах C ++!
    2. (Мой собственный вопрос и ответ): почему `constexpr` является частью прототипа шаблона C ++ 14 для` std :: max () `?
    3. Разница между `constexpr` и` const`

Clang note из Википедии :

[Clang] предназначен для использования в качестве замены для коллекции компиляторов GNU (GCC), поддерживая большинство его флагов компиляции и неофициальных расширений языка.

Габриэль Стейплс
источник
Для downvoter с прошлых 24 часов: хорошие новости! Я удалил мой раздел 4, который я добавил вчера в раздел 3, и вместо этого разместил его здесь . Вы можете переоценить мой ответ и дать ему оценку, если хотите, поскольку я вложил в него много полезной информации и приложил все усилия, чтобы сделать его надежным, полезным, каноническим ответом, который принесет пользу всем. Теперь это снова сфокусировано. :) Спасибо!
Габриэль Стейплс
4

Стоит отметить, я думаю, что если вы определите minи maxс третичными, такими как

#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))

затем получить тот же результат для особого случая, fmin(-0.0,0.0)и fmax(-0.0,0.0)вам нужно поменять местами аргументы

fmax(a,b) = MAX(a,b)
fmin(a,b) = MIN(b,a)
Z бозон
источник
Все еще не будет работать для NaN. fmin(3.0,NaN)==fmin(NaN,3.0)==fmax(3.0,NaN)==fmax(NaN,3.0)==3.0
Грегго
@greggo, я дал лучший ответ здесь stackoverflow.com/a/30915238/2542702
Z бозон
4

Похоже, что Windef.h(а-ля #include <windows.h>) есть макросы maxи min(строчные) макросы, которые также страдают от трудности «двойной оценки», но они там для тех, кто не хочет перекатывать свои собственные :)

rogerdpack
источник
12
Вы даже удивлены?
Matt Joiner
2

Я знаю, что парень сказал "C" ... Но если у вас есть возможность, используйте шаблон C ++:

template<class T> T min(T a, T b) { return a < b ? a : b; }

Введите safe, и никаких проблем с ++, упомянутых в других комментариях.

Бас Куэнен
источник
16
Аргументы должны быть константными ссылками, вы никогда не знаете, что пользователь передаст.
нмихайлов
6
Такая функция уже стандартизирована ( std :: min ).
dreamlax
C ++ имеет много стандартных функций для большинства обычных целей, не изобретайте колесо. Однако MS также определяет свои собственные минимальные / максимальные значения, что иногда вызывает проблемы
phuclv
0

Максимум двух целых aи bесть (int)(0.5((a+b)+abs(a-b))). Это также может работать с (double)и fabs(a-b)для двойников (аналогично для поплавков)

NRZ
источник
Извините, если это не так, я новичок в C, но этот код работает для меня
NRZ
2
Я не уверен, что это работает с не целыми числами. Математика с плавающей точкой имеет нелинейную точность.
Treesrule14
Чтобы расширить комментарий @ Treesrule14: это не работает, потому что компьютеры не обрабатывают числа так же, как математики. С плавающей точкой возникают проблемы с округлением, поэтому вряд ли вы получите правильный ответ. Даже если вы используете целочисленную математику, MAX_INT + MAX_INT дает -2, поэтому max (MAX_INT, MAX_INT) при использовании вашей формулы будет равен -1.
user9876
-3

Самый простой способ - определить ее как глобальную функцию в .hфайле и вызывать ее в любое время, если ваша программа имеет модульную структуру с большим количеством файлов. Если нет, double MIN(a,b){return (a<b?a:b)}это самый простой способ.

srezat
источник
1
@technosaurus Было бы полезно, если бы вы описали, почему это решение неверно, а не только так.
Tur1ng
@technosaurus, ваш ответ действительно бесполезен. В этом случае оказывается, что функция определена совершенно неверно (пропущены типы во входных параметрах, пропущена точка с запятой после оператора return), а преобразование int-входов в double - плохой способ сделать что-либо, поэтому тип не должен быть double. Выражение для определения или оператора было бы лучше здесь (например: см. Здесь ), но если функция, рассмотрите возможность создания одной функции, чтобы сделать это для типов int32_t, одну для типов uint32_t и одну для типов с плавающей запятой или типа double, всего 3 разные функции.
Габриэль Стейплз
1
@GabrielStaples Этот ответ должен быть помечен как не ответ - ничего не поделаешь. Хотя это может быть использовано в качестве примера того, как быть самым неправильным в наименьшем количестве места. Рекомендация глобальных функций в заголовке (не статически даже inline?) Будет нарушать код с 2+ единицами компиляции, даже не компилируется, называя функцию как макрос, подразумевая int как 1989, возвращая double без указания причины, подразумеваемой броски, которые будут вызывать предупреждения в лучшем случае ... и что самое важное, ЭТО НЕ ОТВЕЧАЕТ НА ВОПРОС - не универсальный, не типобезопасный и, безусловно, не самый лучший способ
технозавр
Каждая из этих проблем заслуживает дальнейшей критики, которая не может быть рассмотрена достаточно подробно.
технозавр