Reputation: 2965
I've come across a strange problem with method specialisation.
Given this code...
#include <string>
class X
{
public:
template< typename T >
void set( T v );
};
template<>
void X::set( const std::string & v )
{
}
template<>
void X::set( int v )
{
}
int main( int, char *[] )
{
X x;
std::string y;
x.set( y );
x.set( 1 );
}
When I link it with g++ 4.3.3 I get an undefined reference to
void X::set<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)
.
Which is basically an undefined reference to void X::set( std::string )
.
So my question is, why doesn't the compiler use the specialisation with const std::string &
?
If I explicitly call x.set< const std::string & >( y )
then this compiles and links fine.
Upvotes: 4
Views: 217
Reputation: 11669
Probably this article
will explain the situation.
You might expect the specialization
void X::set( const std::string& )
will participate in overload
resolution.
However, surprisingly,
specializations don't participate in overload resolution.
In the call x.set( y )
, the compiler deduces the type of T
in the primary
template from the argument y
with a type std::string
.
So, the compiler deduces that T
is std::string
, then searches matching
specialization.
However, since std::string
and const std::string&
are different types,
the compiler selects primary template in the end.
After proper primary template is selected, the matching specialization is
selected in the same manner as the case for class template.
The reason that the specialization set( const std::string& )
isn't selected
is similar to that the specialization A< std::string >
isn't selected
in the following code:
template< class > class A; // primary
template<> class A< const std::string& > {}; // specialization
int main() {
A< std::string > a; // This doesn't select the specialization
}
Upvotes: 2
Reputation: 2250
Reverting back to original proposition after compilation:
Your method signature for string should not be a reference. Should be:
template<>
void X::set( const std::string v )
{
}
This is because in your template definition you have specified T paramater
and not T& paramater
Upvotes: 3
Reputation: 76386
It's altogether incorrect syntax. When specializing templates, you have to include the angle brackets with the types you are specializing for. E.g.:
template<typename T>
struct X { ... };
template<>
struct X<int> { ... };
// ^^^^^ see, there have to be angle brackets after the identifier
So
template<>
void X::set(std::string &) { ... }
is not specializing anything, it is implementing
class X {
template<>
void set(std::string &) { ... }
};
which is altogether different function. What I don't understand is why gcc didn't produce an error because the class does not have that member.
Now even if you used the supposedly-correct syntax, it wouldn't be correct, because, as already answered by Tom, you can't specialize functions (just overload with non-template version). In C++03, that is; it is allowed in C++0x.
Upvotes: 3
Reputation: 5309
What you want to use is overloading.
class X
{
public:
void set( int v); //the case for ints
void set( const std::string&) //the case for strings
//The default catch all case.
template< typename T >
void set( T v );
};
//not a template specialisation.
void X::set( int v )
{
}
//not a template specialisation.
void X::set( const std::string & v )
{
}
//the catch all case
template<typename T>
void X::set(T v)
{
}
The non-template operators will be chosen ahead of the template because they provide a better match if available. Otherwise the template will be chosen
Upvotes: 2