56

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 ) {}
jwfearn
  • 27,700
  • 27
  • 94
  • 118

8 Answers8

44

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);
    }
};
Georg Fritzsche
  • 95,426
  • 26
  • 188
  • 233
  • This is similar to the solution I'm using, thanks! I wonder if there's a way to do this less verbosely using Boost? – jwfearn Jan 24 '10 at 20:45
  • 2
    Not really, it doesn't get easier in general. You need a helper function to avoid the issue of *"enclosing class has to be explicitly specialized"* and you need to move that one into a class to do partial specialization. – Georg Fritzsche Jan 25 '10 at 08:40
12

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.

GManNickG
  • 478,574
  • 51
  • 478
  • 539
  • 12
    Maybe it is the C++ standard that is not in the clear... If Microsoft can do it why can't the other compilers? – P i Nov 05 '14 at 00:35
  • It should be pointed out that since this question was answered, C++11 has come out with [`std::is_same<>`](https://en.cppreference.com/w/cpp/types/is_same), which replaces some of the code above. – Ken Y-N Dec 27 '21 at 02:15
2

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;
    }
 };
Yufeng Li
  • 21
  • 1
1

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 ) {}
ephemient
  • 189,938
  • 36
  • 271
  • 385
1

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.

Grwww
  • 31
  • 3
0

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

https://godbolt.org/z/hPbP1M

Mikhail
  • 7,403
  • 10
  • 58
  • 126
0

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.

John Paul
  • 73
  • 6
-1

Try this:

template <> template<typename T> inline void C<T> :: foo<tag2>(T) {}
Alexander Gessler
  • 44,353
  • 6
  • 80
  • 121