C++ 声明不';t解决';实例化后的显式专门化';错误

C++ 声明不';t解决';实例化后的显式专门化';错误,c++,crtp,explicit-specialization,template-instantiation,C++,Crtp,Explicit Specialization,Template Instantiation,假设我正在尝试创建自己的boost::filesystem::path实现,使用: (为简洁起见,给出的代码不完整,但在使用GCC 4.8.4使用“g++-std=c++11-o mypath./mypath.cpp”编译时,会出现上述问题) mypath.hpp: #如果没有我的路径#水电站 #定义MYPATH_水电站 #包括 #包括 名称空间我的{ 模板 类路径库 { 公众: PathBase(); PathBase(std::string const&p); std::string()常量

假设我正在尝试创建自己的boost::filesystem::path实现,使用:

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

mypath.hpp:

#如果没有我的路径#水电站
#定义MYPATH_水电站
#包括
#包括
名称空间我的{
模板
类路径库
{
公众:
PathBase();
PathBase(std::string const&p);
std::string()常量;
布尔IsSeparator(字符c)常量;
std::字符串分隔符()常量;
typedef std::vector pathvec;
受保护的:
pathvec_路径;
私人:
虚拟标准::字符串分隔符()常量=0;
};
类路径:公共路径库
{
公众:
路径();
路径(std::string const&p);
私人:
虚拟std::string _separators()常量final;
};
}//名称空间“my”
#endif//MYPATH\u水电站
mypath.cpp:

#包括“mypath.hpp”
名称空间我的{
//////////模板类路径库;
模板
布尔路径库::IsSeparator(字符c)常量
{
返回(分隔符().find(c)!=std::string::npos);
}
模板
std::string PathBase::Separators()常量
{
返回_分隔符();
}
}//名称空间
int main(int argc,字符**argv)
{
返回0;
}
当然,我发现编写的代码不会编译,因为在
IsSeparator()
隐式实例化它之后,我显式地专门化了
separator()
。但我并不特别想打鼹鼠,试图让我所有的方法都井然有序

在研究类似的问题时,我发现其中一个问题表明,我可以通过简单地声明我的专业知识来巧妙地解决这个问题。但是

  • 我的注释掉了
    模板类PathBase行对问题没有影响,并且
  • 感觉我的头文件已经用它的整个
    类路径:publicpathbase{…}
    声明声明了显式的专门化
    我的显式声明到底应该是什么样子?让我们先解决这些问题:

  • 模板类路径库不声明显式专门化;它是一个显式的实例化定义。您正在请求编译器根据您提供的定义实例化
    PathBase
    及其定义的所有成员。在这种特殊情况下,它实际上没有任何区别

    显式专门化的声明类似于
    模板类PathBase,但这也不是你想要的;见下文

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


  • 在您的cpp文件中,对于隐式实例化的
    PathBase
    ,您显式地专门化了
    IsSeparator
    分隔符。您请求编译器根据您提供的通用定义实例化
    PathBase
    ,但是,当需要这些特定函数的定义时,请使用您提供的特定定义

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

    您需要尽快将这些显式专门化告诉编译器,以免某些代码试图使用这些定义(它需要知道它必须查找特定定义,而不是泛型定义)。您可以通过声明(不一定定义)显式专门化来实现这一点

    最安全的做法是在
    模板类PathBase
    的定义的右括号后面立即执行。比如:

    class Path;
    template<> std::string PathBase<Path>::Separators() const;
    template<> bool PathBase<Path>::IsSeparator(char c) const;
    
    类路径;
    模板std::string PathBase::Separators()常量;
    模板bool PathBase::IsSeparator(char c)const;
    
    您肯定需要在头文件中执行此操作,而不是在cpp文件中,否则使用头文件的其他cpp文件将不知道显式专门化,并将尝试实例化泛型版本(如果需要)。这将使您的程序格式错误,不需要诊断(这也适用于您的示例)。这意味着:如果编译器足够聪明,能够诊断问题,你应该心存感激;如果不是,你就不能抱怨,那还是你的错

    在预先声明了显式专门化之后,可以稍后再进行定义,可能是在一个单独的cpp文件中;这很好,就像正常函数一样

    还要注意的是,如果希望在头文件中包含显式专门化的定义(例如,为了简化内联),则必须将它们声明为
    inline
    ,这与普通函数一样。否则,在多个cpp文件中包含头将导致程序格式错误,NDR(通常在链接时会出现多个定义错误)


    强制性标准引用自:

    […]编写专门化时,请注意其位置;或 要使它编译起来将是一种考验,就像点燃一样
    #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;
    }
    
    class Path;
    template<> std::string PathBase<Path>::Separators() const;
    template<> bool PathBase<Path>::IsSeparator(char c) const;