template
s are not macros. Name lookup of symbols is done in two contexts: first, where you wrote the template
, and second pass of only argument-dependent lookup (ADL, or Koeing Lookup) when you pass the template
a type (when the template
"factory" is instantiated into an actual class
or function).
When the function is in the same namespace as A
, it is found via ADL. When it is not, it will not be. So when you moved the bar
function into space::bar
, it was no longer ADL-found with an argument of type A
.
This is intentional, to both prevent your template
from meaning completely different things at different spots, and to allow non-member interface extensions to types in their own namespace only.
Either use a traits class, stick such helper functions in the same namespace as the type, or pass in functors.
A traits class is probably easiest. Create a class with a static function foo
(if you want a default implementation: otherwise leave empty). Call it from your template
. Specialize it for A
. Now you can implement special behavior for your A
type at that point -- it could even call your space::bar
function if that function is in view.
Putting bar
in the same namespace as A
is another solution, and I find it scales better than traits classes. Many of my personal traits classes end up falling back on an ADL lookup, as that lets me inject the handling code right next to where I define A
. (The use of traits classes lets me also have my trait handle things in std
, where I am not allowed to inject functions for ADL purposes for types living in std
(you can inject ADL functions into std
, but only for your own types))
The functor solution is how std::map
works -- while it falls back on std::less<T>
which falls back on operator<
, it takes a functor that lets you specify how the key type should be compared within this map
.