60

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
  • 28,781
  • 28
  • 95
  • 122

9 Answers9

46

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
  • 97,545
  • 26
  • 194
  • 236
  • 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
  • 494,350
  • 52
  • 494
  • 543
  • 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
3

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
  • 31
  • 1
2

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
  • 51
  • 4
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
  • 198,619
  • 38
  • 280
  • 391
1

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>>> { }; 
};
D.Deriso
  • 4,271
  • 2
  • 21
  • 14
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,749
  • 11
  • 62
  • 136
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
  • 81
  • 6
  • No, it's not the same and you can't remove `template<>` if the template type is returned by the method. – Serge Rogatch Sep 05 '22 at 06:01
  • What Serge Rogatch is saying is wrong. A fully specialized function cannot return a template type. This is because it doesn't have a type parameter. – John Paul Sep 06 '22 at 17:54
  • Removing template and making it function overload works. – bop Sep 06 '22 at 18:04
  • You can write `T x = a.foo()`, that's how the method gets the type parameter. – Serge Rogatch Sep 06 '22 at 21:06
  • Serge Rogatch, you don't need type as a parameter when template is fully specialized. The type is known and no longer a parameter. That is the point. – John Paul Sep 07 '22 at 20:32
  • Serge Rogatch, what is it that you can do with a fully specialized template member function that you can't do with an overloaded member function ? Write the code and show it. – John Paul Sep 07 '22 at 20:54
-2

Try this:

template <> template<typename T> inline void C<T> :: foo<tag2>(T) {}
Alexander Gessler
  • 45,603
  • 7
  • 82
  • 122