C++ syntax for explicit specialization of a template function in a template class?
I have code which works in VC9 (Microsoft Visual C++ 2008 SP1) but not in GCC 4.2 (on Mac):
struct tag {};
template< typename T >
struct C
{
template< typename Tag >
void f( T ); // declaration only
template<>
inline void f< tag >( T ) {} // ERROR: explicit specialization in
}; 开发者_开发问答 // non-namespace scope 'structC<T>'
I understand that GCC would like me to move my explicit specialization outside the class but I can't figure out the syntax. Any ideas?
// the following is not correct syntax, what is?
template< typename T >
template<>
inline void C< T >::f< tag >( T ) {}
You can't specialize a member function without explicitly specializing the containing class.
What you can do however is forward calls to a member function of a partially specialized type:
template<class T, class Tag>
struct helper {
static void f(T);
};
template<class T>
struct helper<T, tag1> {
static void f(T) {}
};
template<class T>
struct C {
// ...
template<class Tag>
void foo(T t) {
helper<T, Tag>::f(t);
}
};
GCC is in the clear, here. MSVC has a non-standard extension that allows in-class specialization. The standard, however, says:
14.7.3.2:
2. An explicit specialization shall be declared in the namespace of which the template is a member, or, for member templates, in the namespace of which the enclosing class or enclosing class template is a member. An explicit specialization of a member function, member class or static data member of a class template shall be declared in the namespace of which the class template is a member.
Additionally, you can't partially specialize a function. (Though I'm unsure about the details in your case, that would be the final blow.)
You could do this:
#include <iostream>
struct true_type {};
struct false_type {};
template <typename T, typename U>
struct is_same : false_type
{
static const bool value = false;
};
template <typename T>
struct is_same<T, T> : true_type
{
static const bool value = true;
};
struct tag1 {};
struct tag2 {};
template< typename T >
struct C
{
typedef T t_type;
template< typename Tag >
void foo( t_type pX)
{
foo_detail( pX, is_same<Tag, tag1>() );
}
private:
void foo_detail( t_type, const true_type& )
{
std::cout << "In tag1 version." << std::endl;
}
void foo_detail( t_type, const false_type& )
{
std::cout << "In not tag1 version." << std::endl;
}
};
int main(void)
{
C<int> c;
c.foo<tag1>(int());
c.foo<tag2>(int());
c.foo<double>(int());
}
Though this is somewhat ugly.
Came across this question. This should work:
struct tag {};
template< typename T >
struct C {
template< typename Tag, typename std::enable_if<std::is_same<Tag, tag>::value, int>::type = 0>
void f( T ){
std::cout<<"tag type" <<std::endl;
}
template< typename Tag, typename std::enable_if<!std::is_same<Tag, tag>::value, int>::type = 0>
void f( T ){
std::cout<<"non tag type" <<std::endl;
}
};
I know this may not satisfy you, but I do not believe you may not have a specialization enclosed within a non-explicitly-specialized structure.
template<>
template<>
inline void C< tag1 >::foo< tag2 >( t_type ) {}
The basic detail is that you need to put the code declaration outside of the class so that there is only one declaration of it. If you leave it in a header, declared for all including c++ source files to see, you end up with multiple instances of the same class defined. Just put the declaration of the templated function in the header file, and then move the declared specializations of that templated function into your C++ source file and all will be good because the compiler will generate the correct references based on the types of specialization you use in your source code.
For example you want to create an extensible Number class like java's Number class so that you can pass numeric values around. If this is in the .h/.hpp file, the compiler will know how to generate references to each specialization because the return type is part of the generated function name that the compiler generates references for.
class Number {
Int32 intVal;
double d;
float f;
Int64 longVal;
std::string strVal;
public:
template<T>
T getValue();
... other functions needed go here...
};
In your C++ source file you can just write the following.
template<>
Int32 Number::getValue() { return intVal; }
template<>
double Number::getValue() { return d; }
template<>
float Number::getValue() { return f; }
template<>
Int64 Number::getValue() { return longVal; }
template<>
std::string Number::getValue() { return strVal; }
Now when you pass a Number around, depending on which value type you assign it to, you can use an appropriate value type on a getValue<>() calls.
From https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85282 (Patrick Palka)
[...] as a workaround, instead of e.g.:
struct A { template<class T> struct B; template<> struct B<int> { }; // unsupported class-scope explicit specialization };
in C++20 one can do:
struct A { template<class T> struct B; template<std::same_as<int> T> struct B<T> { }; };
or in C++17:
struct A { template<class T, class = void> struct B; template<class T> struct B<T, std::enable_if_t<std::is_same_v<int,T>>> { }; };
While the code is perhaps non-compliant, one practical solution is to switch to clang, where it works fine.
https://godbolt.org/z/hPbP1M
gcc doesn't allow member function full specialization inside a class. This is because functionality wise it is the same as function overloading. So just remove template<> from specializations and make them function overloads instead.
Try this:
template <> template<typename T> inline void C<T> :: foo<tag2>(T) {}
精彩评论