Иногда я хочу иметь функцию в заголовочном файле (включенном в несколько различных блоков перевода) без указания компилятору встроить ее (например, в библиотеку только для заголовка).Это легко сделать в стиле C, просто объявив функцию static
, например:
struct somedata { ... }
static somefunc (somedata *self) { ... }
Это позволяет компилятору решать, следует ли встроить функцию или нет, при этом все же допуская несколько определенийфункция в нескольких единицах перевода, так как не имеет внешней связи.А также позволяет мне вызывать эту функцию с somedata
структурами, созданными в других единицах перевода, потому что типы совместимы.
Мой вопрос, как мне сделать это с классами и методами?Например, возьмите этот заголовочный файл, который практически идентичен использованию класса и метода вместо функции и явного указателя объекта:
struct someclass {
void method ();
}
void someclass::method () { ... }
Очевидно, я не могу использовать static someclass::method
, потому чтоэто совсем другое.
Я также не могу поместить это в анонимное пространство имен, потому что тогда я получаю разные типы struct someclass
в разных единицах перевода, то есть я не могу использовать someclass *
из одного файла вдругой, потому что это были бы разные (и несовместимые) типы.
Я могу объявить все эти методы inline
, которые будут работать, но будут иметь нежелательный эффект, в частности, попросить компилятор встроить их дажекогда это не имеет смысла.
Я упускаю что-то очевидное или C ++ не имеет эквивалента static
для методов?На мой взгляд (надеюсь, что это не так), единственные варианты для меня - это либо переместить все эти методы в отдельную единицу перевода, либо пометить их как встроенные - кажется, что нет эквивалента внутренней связи в стиле C.
Обновление: Я думаю, что этот вопрос был преждевременно закрыт как дубликат.Предположительно повторяющийся вопрос заключается в том, будут ли функции маркировки как встроенные всегда встроены в них.Этот вопрос о том, как избежать правила ODR, как это делает статическое ключевое слово для простых функций, или объяснить, что это не может быть сделано в C ++, на который не отвечает другой вопрос, который просто говорит спрашивающему не беспокоиться об этом.В моем вопросе inline упоминается только как возможное (но плохое) решение.
Обновление 2: Несколько раз упоминалось, что inline не является запросом на встраивание функции или чтоСтандарт C использует только inline для обхода правила ODR и не просит компилятора встроить функцию.
Оба утверждения явно не соответствуют действительности.Например, просмотр документации GCC или исходного кода LLVM показывает, что широко используемые компиляторы рассматривают inline
как запрос для вставки функции.Я также цитирую C ++ 03, который говорит (в 7.1.2.2):
[...] Встроенный спецификатор указывает на реализацию, что внутренняя замена тела функции в точке вызова являетсяпредпочтительнее обычного механизма вызова функций.[...]
Таким образом, существующие компиляторы и стандарт C ++ (я только проверил 148882: 2003) явно не согласны с неоднократным утверждением «inline влияет только на ODR».
Такое неправильное восприятие кажетсябыть довольно широко распространенным, как видно, например, здесь: https://blog.tartanllama.xyz/inline-hints/, где кто-то исследует это утверждение, глядя на фактический исходный код GCC / LLVM и обнаруживает, что оба компилятора обрабатывают inline как фактический запрос на встраивание.
ОднакоИмейте в виду, что мой вопрос о том, как получить эффект static
для функций-членов в C ++, или в качестве альтернативы, чтобы получить более четкое утверждение, что C ++ просто не имеет этой функции для функций-членов, только для простых функций.Свойства inline
здесь имеют отношение только в той степени, в которой это решает проблему за счет потенциально нежелательного встраивания, что может ухудшить производительность.
Для меня относительно ясно, что нет способа обойти одно правило определения.Что мне не ясно, так это то, что другого способа добиться этого эффекта нет.Например, следующая лучшая вещь для static
- это анонимное пространство имен, но оно также не работает, так как делает объявленные в нем структуры несовместимыми между различными единицами перевода, поэтому их нельзя взаимозаменять.
Я надеюсь, что может быть способ обойти это, например, с помощью структуры вне анонимного пространства имен и наличия производного класса внутри, или какой-либо другой конструкции, но я не могу видеть, как в данный момент, в то время как наВ то же время я не могу исключить, что это может быть возможно - таким образом, этот вопрос.
Обновление 3 : Чтобы пояснить пример - методы не содержат статических переменных, и это не имеет значенияприводит ли конечный результат к нескольким физическим различным копиям метода или нет, если все такие копии ведут себя одинаково.Фактический пример такого метода:
char *reserve (int bytes)
{
if (left <= bytes)
flush ();
if (left <= bytes)
throw std::runtime_error ("bulkbuf allocation overflow");
return cur;
}
Если этот метод вызывается часто (в источнике), но не часто (во время выполнения), запрос компилятора о включении его без какой-либо причины может бытьвредно для производительности и, конечно, для размера кода.
Обновление 4 : есть многократные утверждения, что компиляторы универсально игнорируют ключевое слово inline как запрос на встраивание, несмотря на убедительные доказательства того, что это неверно,.
Просто чтобы избавиться от каких-либо сомнений, я попробовал это с помощью этой (довольно бессмысленной) программы:
//inline
int f(int i)
{
return i < 0 ? 0 : f(i-1) + 1;
}
int main(int argc, char *[])
{
return f(5) + f(argc);
}
Обратите внимание на закомментированное ключевое слово inline
.
Когда я компилирую это с g ++ 6.3.0 (выпущено в 2016 году) из Debian GNU / Linux Stretch (с использованием g++ -Os -S -o - test.C
), я получаю эту программу main
, когда inline
закомментирован:
movl %edi, %ecx
movl $5, %edi
call f(int)
movl %eax, %edx
movl %ecx, %edi
call f(int)
addl %edx, %eax
ret
И это, когда inline активен в:
xorl %eax, %eax
.L3:
cmpl %eax, %edi
js .L2
incl %eax
jmp .L3
.L2:
addl $6, %eax
ret
Так что без inline
функция не была встроена, с inline
она была встроена.По крайней мере, это доказывает, что компиляторы не всегда игнорируют inline как запрос на встраивание, как часто утверждается (и g ++, безусловно, является одним из немногих основных компиляторов C ++, и версия 6.3 вряд ли устарела, так что это нестранный нишевый компилятор).
Фактически, как стандартный, так и существующий компиляторы обрабатывают inline как нечто большее, чем просто изменение поведения ODR, а именно как явный запрос на включение функции.
Примечанието, что компилятор игнорирует подсказку или нет, имеет отношение только к моему вопросу, касательно языка C ++, а не каких-либо компиляторов, и, по крайней мере, C ++ 03 требует от компиляторов «преимущественно» встроенных функций, помеченных как таковые, не требуя ихчтобы сделать это, поэтому мои опасения по поводу inline действительны независимо от того, игнорируют ли это компиляторы.
Обновление 5:
Изменение f
на это:
вернуть я <0?1: f (i-1) + f (i-2); </p>
приводит к аналогичному поведению как с clang ++ 3.8.1-24, так и с g ++.Кроме того, Всегда ли c ++ 11-совместимые компиляторы всегда игнорируют встроенные подсказки? утверждает, что MSVC также возглавляет ключевое слово inline как запрос фактической вставки.
g ++, clang ++ / LLVM и MSVC вместе покрываютбольшая доля «рынка» C ++, поэтому можно с уверенностью сказать, что компиляторы почти всегда обрабатывают inline как запрос на встраивание, независимо от того, прислушиваются ли они к этому или нет, и в дополнение к другим требованиям стандарта C ++.