声明没有解决 'explicit specialization after instantiation' 错误
Declaration doesn't solve 'explicit specialization after instantiation' error
假设我正在尝试使用 Curiously Recurring Template Pattern:
创建自己的 boost::filesystem::path 实现
(为简洁起见,代码不完整,但在使用 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
mypath.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;
}
当然,我发现编写的代码无法编译,因为我在 IsSeparator()
隐式实例化它之后明确特化了 Separators()
。但我并不是特别想玩打地鼠游戏,试图让我所有的方法都井井有条。
在研究关于 SO 的类似问题时,我发现其中一个 accepted answer 表明我可以通过仅声明我的专业化来巧妙地解决这个问题。但是...
- 我在 mypath.cpp 中注释掉的
template class PathBase<Path>;
行对问题没有影响,
- 有点像我的头文件已经用其整个
class Path : public PathBase<Path> { ... }
声明声明了显式特化。
我的显式声明到底应该是什么样子?
让我们先解决这些问题:
template class PathBase<Path>;
未声明显式特化;它是一个显式实例化定义。您要求编译器实例化 PathBase<Path>
及其具有定义的所有成员,基于您当时提供的定义。在这种特定情况下,它确实没有任何区别。
显式特化的声明看起来像 template<> class PathBase<Path>;
,但这也不是您想要的;见下文。
在定义 Path
时使用 PathBase<Path>
也没有声明显式特化;根据您在上面提供的定义,它会触发 PathBase<Path>
的 隐式实例化 。 class 模板的隐式实例化实例化 class 定义,并且仅实例化其成员函数的声明;它不会尝试实例化函数的定义;这些仅在需要时实例化,稍后再实例化。
在您的 cpp 文件中,您明确地将 IsSeparator
和 Separators
特化为隐式实例化的 PathBase<Path>
。您要求编译器根据您提供的通用定义实例化 PathBase<Path>
,但是,当需要这些特定函数的定义时,请使用您提供的特定定义。
当 class 的结构和大多数成员的通用定义都很好时,它基本上是 shorthand 显式特化整个 class 模板的替代方法,而且您只想fine-tune定义几个成员。如果您显式特化整个 class 模板,则必须为特化的所有成员函数提供单独的 class 定义和定义,这意味着不必要的 copy-paste.
您需要尽快告诉编译器这些显式特化,以免某些代码尝试使用这些定义(它需要知道它必须寻找特定定义而不是通用定义)那些)。您可以通过声明(不一定定义)显式专业化来做到这一点。
最安全的做法是紧跟在 template <class T> class PathBase
定义的右大括号之后。类似于:
class Path;
template<> std::string PathBase<Path>::Separators() const;
template<> bool PathBase<Path>::IsSeparator(char c) const;
你绝对需要在 header 文件中执行此操作,而不是在 cpp 文件中,否则使用 header 的其他 cpp 文件将不知道显式特化并会尝试实例化通用版本(如果他们需要的话)。这将使您的程序 ill-formed,不需要诊断(这也适用于您的示例)。这意味着:如果编译器足够聪明,可以诊断出问题,你应该感激不尽;如果不是,你不能抱怨,这仍然是你的错。
预先声明显式特化后,定义可以稍后出现,可能在单独的 cpp 文件中;没关系,就像正常功能一样。
另请注意,如果您想在 header 文件中包含显式特化的定义(例如,为了简化内联),您必须声明它们 inline
,再次像正常功能一样。否则,在多个 cpp 文件中包含 header 将使程序 ill-formed、NDR(您通常会在 link 时遇到多个定义错误)。
来自[temp.expl.spec]/7的强制性标准报价:
[...] When writing a specialization, be careful about its location; or
to make it compile will be such a trial as to kindle its
self-immolation.
是的,标准化委员会的成员也是人。
假设我正在尝试使用 Curiously Recurring Template Pattern:
创建自己的 boost::filesystem::path 实现(为简洁起见,代码不完整,但在使用 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
mypath.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;
}
当然,我发现编写的代码无法编译,因为我在 IsSeparator()
隐式实例化它之后明确特化了 Separators()
。但我并不是特别想玩打地鼠游戏,试图让我所有的方法都井井有条。
在研究关于 SO 的类似问题时,我发现其中一个 accepted answer 表明我可以通过仅声明我的专业化来巧妙地解决这个问题。但是...
- 我在 mypath.cpp 中注释掉的
template class PathBase<Path>;
行对问题没有影响, - 有点像我的头文件已经用其整个
class Path : public PathBase<Path> { ... }
声明声明了显式特化。
我的显式声明到底应该是什么样子?
让我们先解决这些问题:
template class PathBase<Path>;
未声明显式特化;它是一个显式实例化定义。您要求编译器实例化PathBase<Path>
及其具有定义的所有成员,基于您当时提供的定义。在这种特定情况下,它确实没有任何区别。显式特化的声明看起来像
template<> class PathBase<Path>;
,但这也不是您想要的;见下文。在定义
Path
时使用PathBase<Path>
也没有声明显式特化;根据您在上面提供的定义,它会触发PathBase<Path>
的 隐式实例化 。 class 模板的隐式实例化实例化 class 定义,并且仅实例化其成员函数的声明;它不会尝试实例化函数的定义;这些仅在需要时实例化,稍后再实例化。
在您的 cpp 文件中,您明确地将 IsSeparator
和 Separators
特化为隐式实例化的 PathBase<Path>
。您要求编译器根据您提供的通用定义实例化 PathBase<Path>
,但是,当需要这些特定函数的定义时,请使用您提供的特定定义。
当 class 的结构和大多数成员的通用定义都很好时,它基本上是 shorthand 显式特化整个 class 模板的替代方法,而且您只想fine-tune定义几个成员。如果您显式特化整个 class 模板,则必须为特化的所有成员函数提供单独的 class 定义和定义,这意味着不必要的 copy-paste.
您需要尽快告诉编译器这些显式特化,以免某些代码尝试使用这些定义(它需要知道它必须寻找特定定义而不是通用定义)那些)。您可以通过声明(不一定定义)显式专业化来做到这一点。
最安全的做法是紧跟在 template <class T> class PathBase
定义的右大括号之后。类似于:
class Path;
template<> std::string PathBase<Path>::Separators() const;
template<> bool PathBase<Path>::IsSeparator(char c) const;
你绝对需要在 header 文件中执行此操作,而不是在 cpp 文件中,否则使用 header 的其他 cpp 文件将不知道显式特化并会尝试实例化通用版本(如果他们需要的话)。这将使您的程序 ill-formed,不需要诊断(这也适用于您的示例)。这意味着:如果编译器足够聪明,可以诊断出问题,你应该感激不尽;如果不是,你不能抱怨,这仍然是你的错。
预先声明显式特化后,定义可以稍后出现,可能在单独的 cpp 文件中;没关系,就像正常功能一样。
另请注意,如果您想在 header 文件中包含显式特化的定义(例如,为了简化内联),您必须声明它们 inline
,再次像正常功能一样。否则,在多个 cpp 文件中包含 header 将使程序 ill-formed、NDR(您通常会在 link 时遇到多个定义错误)。
来自[temp.expl.spec]/7的强制性标准报价:
[...] When writing a specialization, be careful about its location; or to make it compile will be such a trial as to kindle its self-immolation.
是的,标准化委员会的成员也是人。