While writing template methods, i ran into the folowing behavior, which i do not understand.
I have the folowing code:
#include <array>
#include <iostream>
namespace A
{
template<typename T>
class Bar
{
T Var;
};
}
namespace B
{
template<typename T>
void Foo(const T& var)
{
std::cout << "default template method has been called" << std::endl << std::endl;
}
template<typename T, size_t N>
void Foo(const std::array<T,N>& var)
{
std::cout << "Array overload has been called" << std::endl;
for(auto& elem : var)
{
Foo(elem);
}
}
template<typename T>
void Foo(const A::Bar<T>& var)
{
std::cout << "Bar overload has been called" << std::endl << std::endl;
}
}
int main()
{
int VarInt;
A::Bar<int> VarBar;
std::array<int, 1> ArrayInt;
std::array<A::Bar<int>, 1> ArrayBar;
B::Foo(VarInt);
B::Foo(VarBar);
B::Foo(ArrayInt);
B::Foo(ArrayBar);
return 0;
}
It's output is not what i expect as with the array of Bar, the default template is called instead of the Bar overload:
default template method has been called
Bar overload has been called
Array overload has been called
default template method has been called
Array overload has been called
default template method has been called
I noticed that doing the folowing enables the compiler to find the correct overloads :
- removing all namespaces or
- declaring all the templates prototypes before implementing them
CodePudding user response:
It's output is not what i expect as with the array of Bar, the default template is called instead of the Bar overload
For the call expression, B::Foo(ArrayBar)
, the 2nd overload void Foo(const std::array<T,N>& var)
is chosen with N
deduced to 1
and T
deduced to A::Bar<int>
.
Now inside that overload, when the call expression Foo(elem)
is encountered, the compiler doesn't know about the third overloaded Foo
. Thus it chooses the first overload of Foo
which is the most general among the two available at that point.
declaring all the templates prototypes before implementing them
Providing the declarations for all the overloads of Foo
before implementing them, lets the compiler know that there is a third overload of Foo
. So this time, the call expression Foo(elem)
calls the third overloaded Foo
as expected(because it is more special than the first overload of Foo
).
removing all namespaces
And when everything is put in the same global namespace, due to ADL the third overload is also found.