C++ syntax for explicit specialization of a template function in a template class?

C++TemplatesGcc

C++ Problem Overview


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 ) {}

C++ Solutions


Solution 1 - C++

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);
    }
};

Solution 2 - C++

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.

Solution 3 - C++

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;
    }
 };

Solution 4 - C++

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 ) {}

Solution 5 - C++

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.

Solution 6 - C++

While the code is perhaps non-compliant, one practical solution is to switch to clang, where it works fine.

https://godbolt.org/z/hPbP1M

Solution 7 - C++

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.

Solution 8 - C++

Try this:

template <> template<typename T> inline void C<T> :: foo<tag2>(T) {}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionjwfearnView Question on Stackoverflow
Solution 1 - C++Georg FritzscheView Answer on Stackoverflow
Solution 2 - C++GManNickGView Answer on Stackoverflow
Solution 3 - C++Yufeng LiView Answer on Stackoverflow
Solution 4 - C++ephemientView Answer on Stackoverflow
Solution 5 - C++GrwwwView Answer on Stackoverflow
Solution 6 - C++MikhailView Answer on Stackoverflow
Solution 7 - C++John PaulView Answer on Stackoverflow
Solution 8 - C++Alexander GesslerView Answer on Stackoverflow