Sometimes, I want to have a function in a header file (included in multiple different translation units) without telling the compiler to inline it (for example in a header-only library). This is easy when doing it the C-style way, just declare the function static
, e.g.:
struct somedata { ... }
static somefunc (somedata *self) { ... }
This lets the compiler decide whether to inline the function or not, while still allowing multiple definitions of the function in multiple translation units, as it has no external linkage. And also allows me to call this function with somedata
structs created in other translation units, because the types are compatible.
My question is, how do I do this with classes and methods? For example, take this header file, which is practically the same thing using a class and a method instead of a function and an explicit object pointer:
struct someclass {
void method ();
}
void someclass::method () { ... }
Obviously, I can't use static someclass::method
because that is something else entirely.
I also cannot put this into an anonymous namespace, because then I get different struct someclass
types in different translation units, i.e. I couldn't use a someclass *
from one file in another, because they would be different (and incompatible) types.
I can declare all these methods inline
, which would work, but would have the undesirable effect of, well, asking the compiler to inline them even when that makes no sense.
Am I missing something obvious, or does C++ not have the equivalent of static
for methods? As I see it (hoping to be wrong), the only options for me are to either move all these methods into a separate translation unit or mark them all as inline - there seems to be no equivalent of C-style internal linkage.
Update: I think this question was closed prematurely as duplicate. The supposedly duplicate question is about whether marking functions as inline will always inline them. This question is about how to avoid the ODR rule as the static keyword for plain functions does, or explain that this cannot be done in C++, neither of which is answered by the other question, which simply tells the asker to not worry about it. In my question, inline is only mentioned as a possible (but bad) solution.
Update 2: It's been mentioned multiple times that inline is nnot a request for function inlining, or that the C standard only uses inline to get around the ODR rule and does not ask the compiler to inline a function.
Both statements are clearly untrue. For example perusing GCC documentation or LLVM source code reveals that widely used compilers do consider inline
as a request to inline a function. I also quote from C++03, which says (in 7.1.2.2):
[...] The inline specifier indicates to the implementation that inline substitution of the function body at the point of call is to be preferred to the usual function call mechanism. [...]
So existing compilers and the C++ standard (I only checked 148882:2003) clearly disagree with the repeated claim of "inline only affects ODR".
This wrong perception seems to be quite widespread, as seen e.g. here: https://blog.tartanllama.xyz/inline-hints/ where somebody investigates this claim by looking at actual GCC/LLVM source code and finds that both compilers treat inline as an actual inlining request.
However, keep in mind that my question is about how to get the effect of static
for member functions in C++, or alternatively to get a more definitive statement that C++ simply doesn't have this feature for member functions, only for plain functions. The properties of inline
are only relevant here to the extent that it does solve the problem at the expense of potentially unwanted inlining, which can be bad for performance.
It's relatively clear to me that there is no way around the one definition rule. What is not clear to me is whether there is really no other way to achieve this effect. For example, the next best thing to static
is an anonymous namespace, but that doesn't work either, as it makes the structs declared in it all incompatible between different translation units, so they can't be interchanged.
I hope there might be a way around it, for example, by having the struct outside the anonymous namespace and having a derived class inside, or some other construct, but I can't see how at the moment, while at the same time I cannot rule out that it might be possible - thus this question.
Update 3: To clarify the example - the methods do not contain static variables, and it doesn't matter whether the end result results in multiple physical different copies of a method or not, as long as all such copies behave the same. An actual example of such a method would be:
char *reserve (int bytes)
{
if (left <= bytes)
flush ();
if (left <= bytes)
throw std::runtime_error ("bulkbuf allocation overflow");
return cur;
}
If this method is called often (in the source), but not often (at runtime), asking the compiler to inline it for no reason could be detrimental to performance and certainly to code size.
Update 4: There are many repeated claims that compilers universally ignore the inline keyword as a request for inlining, despite good evidence that this is incorrect,.
Just to make get rid of any doubts, I tried it out with this (rather nonsensical) program:
//inline
int f(int i)
{
return i < 0 ? 0 : f(i-1) + 1;
}
int main(int argc, char *[])
{
return f(5) + f(argc);
}
Note the commented out inline
keyword.
When I compile this with the g++ 6.3.0 (released 2016) from Debian GNU/Linux Stretch (using g++ -Os -S -o - test.C
), I get this main
program when inline
is commented out:
movl %edi, %ecx
movl $5, %edi
call f(int)
movl %eax, %edx
movl %ecx, %edi
call f(int)
addl %edx, %eax
ret
And this when inline is active in:
xorl %eax, %eax
.L3:
cmpl %eax, %edi
js .L2
incl %eax
jmp .L3
.L2:
addl $6, %eax
ret
So without inline
the function did not get inlined, with inline
, it did get inlined. At the very least, this proves that compilers do not universally ignore inline as a request for inlining, as often claimed (and g++ is certainly one of the few major C++ compilers out there, and version 6.3 is hardly obsolete, so this is not a weird niche compiler).
So fact is, both the standard and existing compilers do treat inline as more than just an ODR behaviour change, namely as an explicit request to inline the function.
Note that whether a compiler ignores the hint or not is only tangentially relevant to my question, which is about the C++ language, not any compilers, and at least C++03 requires compilers to "preferentially" inline functions marked as such, without requiring them to do so, so my concerns with inline are valid whether compilers ignore it or not.
Update 5:
Changing f
to this:
return i < 0 ? 1 : f(i-1) + f(i-2);
results in the analog behaviour with both clang++ 3.8.1-24 and g++. Also, Do c++11-compatible compilers always ignore inline hints? claims MSVC also heads the inline keyword as request for actual inlining.
g++, clang++/LLVM and MSVC together cover a large share of the C++ "market", so it's safe to say that compilers almost universally treat inline as request for inlining, whether they heed it or not, and in addition to the other requirements from the C++ standard.