2

I cannot compile the following code with GCC 7.3:

    template <class C>
    class BasicScalarFormatter
    {
    public:

        typedef std::basic_string<C> String;

        template<typename T>
        static String ToString(T val)
        {
            return String{};
        }

    };

    template<class C>
    typename BasicScalarFormatter<C>::String BasicScalarFormatter<C>::ToString<bool>(bool val)
    {
        return String{};
    }

What is wrong?

With VC2017 it compiles if ToString is defined inside the class:

    template <class C>
    class BasicScalarFormatter
    {
    public:

        typedef std::basic_string<C> String;

        template<typename T>
        static String ToString(T val)
        {
            return ToBasicString<C, T>(val);
        }

        template<>
        static String ToString(bool val)
        {
            return String{};
        }
    }

to make it compile with GCC I moved ToString outside of the class, but it still does not compile. GCC error message is:

ource_file.cpp:21:98: error: template-id ‘ToString<bool>’ in declaration of primary template
         typename BasicScalarFormatter<C>::String BasicScalarFormatter<C>::ToString<bool>(bool val)
                                                                                                  ^
source_file.cpp:21:50: error: prototype for ‘BasicScalarFormatter<C>::String BasicScalarFormatter<C>::ToString(bool)’ does not match any in class ‘BasicScalarFormatter<C>’
         typename BasicScalarFormatter<C>::String BasicScalarFormatter<C>::ToString<bool>(bool val)
                                                  ^
source_file.cpp:14:27: error: candidate is: template<class C> template<class T> static BasicScalarFormatter<C>::String BasicScalarFormatter<C>::ToString(T)
             static String ToString(T val)

see it online here.

Mat
  • 202,337
  • 40
  • 393
  • 406
Alexey Starinsky
  • 3,699
  • 3
  • 21
  • 57

3 Answers3

3

ToString is a template member of another template class.

You cannot specialize an inner template without specializing the outer template, first. That is, you have to specialize some particular instance of BasicScalarFormatter first, then take that and only then you can specialize a particular instance of its member template method. But that specialization would only apply, of course, to the specialized BasicScalarFormatter.

Your obvious intent is to not specialize the outer template, but rather have a specialization for this class member, of all instances of the outer template class.

There aren't really clean solutions here, but this general design pattern is usually simple enough that smart compilers will end up optimizing away the extra redirection:

#include <string>

template<typename C, typename T> class ToStringHelper {

public:

    static std::basic_string<C> helper()
    {
        return std::basic_string<C>();
    }
};

template<typename C> class ToStringHelper<C, bool> {

public:

    static std::basic_string<C> helper()
    {
        return std::basic_string<C>();
    }
};

template <class C>
class BasicScalarFormatter
{
public:

        typedef std::basic_string<C> String;

        template<typename T>
        static String ToString(T val)
        { 
            return ToStringHelper<C,T>::helper();
        }
};

void foo()
{
    BasicScalarFormatter<char> c;

    c.ToString(0);

    c.ToString(true);
}

So, at this point you wind up in static method of a helper class. Your eventual goal is obviously to use something that's a member of the original template. Well, you can always pass this to this helper(), and have it do something with it, or use it to call a method of the invoking class.

And then, as I said, hope that your compiler will get rid of this extra level of indirection, perhaps with some inline keywords sprinkled here and there, to encourage it, or use brute force and use your compiler's extension to force it to inline everything.

Tested with gcc 8.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
0

You lack one template keyword as You defined function template inside class template. This should work:

template<class C>
template<>
typename BasicScalarFormatter<C>::String BasicScalarFormatter<C>::ToString<bool>(bool val)
{
    return String{};
}
bartop
  • 9,971
  • 1
  • 23
  • 54
0

You are lacking a template <typename C> as you are implementing a method of a a class with template parameter C.

Try like this:

#include <string>

template <class C>
class BasicScalarFormatter
{
public:

    typedef std::basic_string<C> String;

    template<typename T>
    static String ToString(T val);

};

template <typename C>
template <typename T>
typename BasicScalarFormatter<C>::String BasicScalarFormatter<C>::
ToString(T val)
{
    return String{};
}

which should compile with GCC 7.3, see here