首页 > 解决方案 > GCC 7中模板类的模板成员函数的特化

问题描述

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

为了使它与 GCC 一起编译,我将 ToString 移到了类之外,但它仍然无法编译。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)

在这里在线查看

标签: c++gcc

解决方案


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

首先,如果不专门化外部模板,就无法专门化内部模板。也就是说,您必须先专门化某个特定实例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给 this helper(),让它用它做一些事情,或者用它来调用调用类的方法。

然后,正如我所说,希望您的编译器摆脱这种额外的间接级别,可能会在inline各处散布一些关键字来鼓励它,或者使用蛮力并使用编译器的扩展来强制它内联所有内容。

用 gcc 8 测试。


推荐阅读