In the code below, foo
should be a function accessible by anyone, but foo_helper
should not, which is why I've put it in an anonymous namespace. Obviously I'm leaving out include guards and includes in this example, but they are there.
foo.h
:
namespace
{
void foo_helper() {}
template <typename T, typename... Tail>
void foo_helper(T head, Tail... tail)
{
bar(head);
foo_helper(tail...);
}
}
void foo();
template <typename... Args>
void foo(Args... args)
{
before();
foo_helper(args...);
after();
}
foo.cpp
:
void foo() {}
The problem is that in order for foo_helper
's variadic template to work, it needs to have that initial version with no argument. But, this forces me to define a non-template function is a header file, which would break after including this file in multiple source files. I cannot move the definition of foo_helper
to a source file because it is in an anonymous namespace, since it is not supposed to be accessible.
Is there a way to solve this issue?
inline void foo_helper() {};
solves your problem.
inline
mostly means "conflicting definitions of this function are to be discarded, and one of the versions kept".
It also non-bindingly suggests "inlining" in a vague way (in that the standard doesn't really cover what inlining is). Compilers may or may not pay attention to that suggestion.
Note that an anonymous namespace does not "make it unusable" or whatever. Anonymous namespaces are designed to block linker collisions, and that is about it. Create a namespace called details
and ... well, trust users not to go and poke inside.
Using an anonymous namespace in a header is a very bad idea.
If there is an inline
function (or template function) within another header file that accesses a symbol or function within the anonymous namespace, you are almost certainly going to have an ODR (one definition rule) violation. That is where the same object, function, etc has two definitions that differ, and isn't allowed to.
For example:
inline void bob() {
foo(1,2,3);
}
if that is #include
d in two different .cpp files, you just made an ill-formed program (no diagnostic required).
Often such ill formed programs "behave the way you expect", but sometimes they do not. As an example, if somewhere along the line you get a static
local variable whose existence depends on ODR violation, you can have multiple compilation units disagree asto which one exists and what its properties are.
In a more general sense, the link order of your program could change its behavior, as different definitions are "chosen" (with possibly extremely subtle differences). Or the phase of the moon could do the same.
ODR violations are surprisingly benign, until they byte you with non-local bugs that are hard to track down.