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

Specialization of a template member function of a template class in GCC 7

本文关键字:函数 专用 成员 GCC      更新时间:2023-10-16

我无法使用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)

在这里在线查看。

>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传递给这个helper(),并让它对它做一些事情,或者用它来调用调用类的方法。

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

使用 gcc 8 进行测试。

您缺少一个template关键字,因为您在类模板中定义了函数模板。这应该有效:

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

您缺少template <typename C>,因为您正在使用模板参数C实现 a 类的方法。

尝试如下:

#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编译,看这里