GCC 7中模板类的模板成员函数的专业化

时间:2018-09-10 11:39:06

标签: c++ gcc

我无法使用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{};
    }

怎么了?

在VC2017中,如果在类内定义了ToString,它将编译:

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

make it compile with GCC I moved ToString outside of the class,但仍然无法编译。 GCC错误消息是:

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)

online here看到它。

3 个答案:

答案 0 :(得分:3)

ToString是另一个模板类的模板成员。

如果不对外部模板 first 进行特殊化,就不能对内部模板进行特殊化。也就是说,您必须先对BasicScalarFormatter的某些特定实例进行特殊化,然后采用该特殊实例,然后才能对其成员模板方法的特定实例进行特殊化。但是,该专门化当然仅适用于专门化的BasicScalarFormatter

您的明显意图是不对外部模板类的所有实例进行专门化,而是对该类成员进行专门化。

这里没有真正干净的解决方案,但是这种通用设计模式通常足够简单,以至于智能编译器最终都会优化掉额外的重定向:

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

因此,在这一点上,您进入了助手类的静态方法。您最终的目标显然是使用原始模板的成员。好吧,您始终可以将this传递给此helper(),并使其执行某些操作,或使用它来调用调用类的方法。

然后,就像我说的那样,希望您的编译器摆脱这种额外的间接访问级别,也许是在各处散布一些inline关键字来鼓励它,或者使用蛮力并使用编译器的扩展以强制其内联所有内容。

经过gcc 8测试。

答案 1 :(得分:0)

在类模板中定义函数模板时,缺少一个template关键字。这应该起作用:

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

答案 2 :(得分:0)

在实现带有模板参数template <typename C>的类的方法时,缺少C

尝试这样:

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

应使用GCC 7.3进行编译,请参见here