声明无法解决"实例化后的显式专用化"错误



假设我正在尝试使用奇怪的重复模板模式创建自己的boost::filesystem::p ath实现:

(为简洁起见,代码不完整,但使用 GCC 4.8.4 使用 'g++ -std=c++11 -o mypath ./mypath.cpp' 编译时会出现问题)

mypath.hpp:

#ifndef MYPATH_HPP
#define MYPATH_HPP
#include <string>
#include <vector>
namespace my {
template <class T>
class PathBase
{
public:
PathBase();
PathBase(std::string const& p);
std::string String() const;
bool IsSeparator(char c) const;
std::string Separators() const;
typedef std::vector<std::string> pathvec;
protected:
pathvec _path;
private:
virtual std::string _separators() const =0;
};

class Path : public PathBase<Path>
{
public:
Path();
Path(std::string const& p);
private:
virtual std::string _separators() const final;
};
} // namespace 'my'
#endif // MYPATH_HPP

我的路径.cpp:

#include "mypath.hpp"
namespace my {
//////////template class PathBase<Path>;
template<>
bool PathBase<Path>::IsSeparator(char c) const
{
return (Separators().find(c) != std::string::npos);
}
template <>
std::string PathBase<Path>::Separators() const
{
return _separators();
}
} // namespace
int main(int argc, char** argv)
{
return 0;
}

当然,我发现编写的代码无法编译,因为我在隐式实例化Separators()之后IsSeparator()显式专用化了它。 但我不是特别想玩打鼹鼠,试图保持我所有方法的有序。

在研究关于SO的类似问题时,我发现其中一个问题的公认答案表明,我可以通过简单地声明我的专业来巧妙地解决这个问题。 但。。。

  1. 我在mypath中注释掉的template class PathBase<Path>;行.cpp对问题没有影响,并且
  2. 感觉我的头文件已经用它的整个class Path : public PathBase<Path> { ... }声明声明了显式专用化。

我的明确声明究竟需要是什么样子?

让我们先把这些排除在外

  1. template class PathBase<Path>;不声明显式专用化;它是一个显式实例化定义。你请求编译器根据你到目前为止提供的定义实例化PathBase<Path>及其具有定义的所有成员。在这种特定情况下,它确实没有任何区别。

    显式专业化的声明看起来像template<> class PathBase<Path>;,但这也不是你想要的;见下文。

  2. 在定义Path时使用PathBase<Path>也不会声明显式专用化;它会根据您上面提供的定义触发PathBase<Path>隐式实例化。类模板的隐式实例化实例化类定义,并且仅实例化其成员函数的声明;它不会尝试实例化函数的定义;这些仅在以后需要时才实例化。


在您的 cpp 文件中,您显式地专门化IsSeparatorSeparators隐式实例化PathBase<Path>。你请求编译器根据你提供的泛型定义实例化PathBase<Path>,但是,当需要这些特定函数的定义时,请使用你提供的特定定义。

它基本上是显式专用于整个类模板的速记替代方法,当类的结构和成员的大多数泛型定义都很好时,您只想微调少数成员的定义。如果显式专用化整个类模板,则必须为专用化的所有成员函数提供单独的类定义和定义,这意味着不必要的复制粘贴。

您需要尽快告诉编译器这些明确的专用化,然后某些代码才有可能尝试使用这些定义(它需要知道它必须查找特定的定义而不是泛型定义)。您可以通过声明(不一定定义)显式专业化来做到这一点。

最安全的地方是在template <class T> class PathBase定义的右括号之后立即。像这样:

class Path;
template<> std::string PathBase<Path>::Separators() const;
template<> bool PathBase<Path>::IsSeparator(char c) const;

您肯定需要在头文件中执行此操作,而不是在 cpp 文件中执行此操作,否则使用该标头的其他 cpp 文件将不知道显式专用化,并将尝试实例化通用版本(如果需要它们)。这将使您的程序格式不正确,无需诊断(这也适用于您的示例)。这意味着:如果编译器足够聪明来诊断问题,你应该感激;如果不是,你不能抱怨,这仍然是你的错。

预先声明了显式专用化后,定义可以稍后出现,可能在单独的 cpp 文件中;这很好,就像普通函数一样。

另请注意,如果要在头文件中包含显式专用化的定义(例如,为了便于内联),则必须将它们声明inline,就像普通函数一样。否则,在多个 cpp 文件中包括标头将使程序格式不正确,NDR(通常在链接时会收到多个定义错误)。


强制性标准引用来自 [temp.expl.spec]/7:

[...]在编写专业化时,请注意它的位置;或 使它编译将是这样的试验,以点燃它的 自焚。

是的,标准化委员会的成员也是人。

相关内容

  • 没有找到相关文章

最新更新