2

I have some code that boils down to this:

#include <type_traits>

struct CByteArray {};

struct CIODevice {
    template <typename T>
    CIODevice& operator<< (T value)
    {
        static_assert(std::is_pod<T>::value, "This method is only intended for POD types");
        return *this;
    }

    template <>
    CIODevice& operator<< (CByteArray data)
    {
        return *this;
    }

    template <typename T>
    CIODevice& operator>> (T& value)
    {
        static_assert(std::is_pod<T>::value, "This method is only intended for POD types");
        return *this;
    }
};

int main()
{
    CIODevice device;
    int i = 0;
    device << i;
    device >> i;
    return 0;
}

It compiles in MSVC, but in GCC I get this:

prog.cpp:13:12: error: explicit specialization in non-namespace scope ‘struct CIODevice’
  template <>
            ^
prog.cpp:20:11: error: too many template-parameter-lists
  CIODevice& operator>> (T& value)
           ^
prog.cpp: In function ‘int main()’:
prog.cpp:32:9: error: no match for ‘operator>>’ (operand types are ‘CIODevice’ and ‘int’)
  device >> i;
     ^

Live sample.

I don't get it, what's the mistake here?

Niall
  • 30,036
  • 10
  • 99
  • 142
Violet Giraffe
  • 32,368
  • 48
  • 194
  • 335
  • (BTW, SFINAE is a more elegant way to handle this case than specializing the whole class and is cross platform. See http://en.cppreference.com/w/cpp/types/enable_if, and welcome to the rabbit hole that is templates. – IdeaHat Nov 04 '14 at 13:24
  • @MadScienceDreams: I'm not specializing the class, I'm only specializing one template method of a non-template class. I've also spent the last 2 days (literally) on learming how to use `enable_if`, then understanding why it doesn't work the way I expected, and then getting rid of it in favor of another template trick. So please, no more `enable_if` today :) – Violet Giraffe Nov 04 '14 at 13:27
  • 2
    This isn't a duplicate question. The referenced question is about specializing a member function template inside a class template, and this is explicitly about a non-template class. – John Neuhaus May 27 '16 at 13:45

2 Answers2

4

C++ specification § 14.7.3 (excerpt);

  1. An explicit specialization of any of the following: ...
    • member function template of a class or class template ...
  2. An explicit specialization shall be declared in a namespace enclosing the specialized template.

Basically, it must be declared in the namespace scope (as GCC and Clang require).

struct CIODevice {
    template <typename T>
    CIODevice& operator<< (T value)
    {
        static_assert(std::is_pod<T>::value, "This method is only intended for POD types");
        return *this;
    }

    template <typename T>
    CIODevice& operator>> (T& value)
    {
        static_assert(std::is_pod<T>::value, "This method is only intended for POD types");
        return *this;
    }
};

template <>
CIODevice& CIODevice::operator<< (CByteArray data)
{
    return *this;
}

Sample code.

Niall
  • 30,036
  • 10
  • 99
  • 142
  • @VioletGiraffe. Sorry. No idea on the motivation, good point though, now that you ask I also wonder why. – Niall Nov 04 '14 at 13:31
  • 1
    Oh! I didn't realize I can define the specialization without even declaring it within the class! That syntax is totally unexpected to me. Thanks. I still wonder why it's designed this way. Seems counter-intuitive. – Violet Giraffe Nov 04 '14 at 13:35
  • 1
    @VioletGiraffe I would say the reason is that defining the specialisation does not actually add an extra member to the class, but it would look like it does. A specialisation is not a different function, it's a recipe for creating a function out of the template for one particular set of template arguments. It's a property of the template, so to speak. – Angew is no longer proud of SO Nov 04 '14 at 13:54
1

See also this.

Basically, MSVC is wrong and GCC is right, you can not do that.

Why don't you use overloading instead of specialization? Try:

template <typename T>
CIODevice& operator<< (T value)
{
    static_assert(std::is_pod<T>::value, "This method is only intended for POD types");
    return *this;
}

// template <> // <--- Remove this
CIODevice& operator<< (CByteArray data)
{
    return *this;
}
Community
  • 1
  • 1
sbabbi
  • 11,070
  • 2
  • 29
  • 57
  • Nice suggestion, I like the simplicity of the overload - it changes the resolution rules a little, but probably for the better. +1 – Niall Nov 04 '14 at 13:34