3

私が使用して、ブースト::ファイルシステム::パスの私の独自の実装を作成しようとしていますと仮定Curiously Recurring Template Pattern宣言が解決しないエラー「明示的な特殊化をインスタンス化した後」

(コードは、簡潔にするために不完全与えられているが、述べたように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は私の専門性を宣言するだけでこの問題をきちんと解決できることを示唆していました。しかし...それはちょっと私のヘッダファイルのように感じている

  1. mypath.cppで私のコメントアウトtemplate class PathBase<Path>;ラインが問題に影響を及ぼさなかった、と
  2. はすでにその全体class Path : public PathBase<Path> { ... }宣言と明示的な特殊化を宣言します。

明確に宣言する必要がありますか?

答えて

3

のは、最初の方法のうち、これらを取得してみましょう:

  1. template class PathBase<Path>;は、明示的な特殊化を宣言していません。 明示的なインスタンス化の定義です。あなたは、コンパイラがPathBase<Path>とそのポイントまで提供した定義に基づいて定義を持つすべてのメンバーをインスタンス化するよう要求しています。この特定のケースでは、実際には何の違いもありません。

    明示的な特殊化の宣言はtemplate<> class PathBase<Path>;のようになりますが、これはあなたが望むものではありません。下記参照。

  2. Pathを定義するときにPathBase<Path>を使用しても、明示的な特殊化は宣言されません。上で指定した定義に基づいて、暗黙のインスタンスPathBase<Path>にトリガーされます。クラステンプレートの暗黙のインスタンス化は、クラス定義とそのメンバ関数の宣言のみをインスタンス化します。関数の定義をインスタンス化しようとはしません。それらは後で必要になったときにのみインスタンス化されます。あなたのcppファイルで


、暗黙的にPathBase<Path>をインスタンス化するためには、明示的IsSeparatorSeparatorsを専門としています。コンパイラが指定した汎用定義に基づいて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ファイルにヘッダーを含めると、プログラムが不正な形式になります(通常、リンク時に複数の定義エラーが発生します)。 [temp.expl.spec]/7から


必須標準の引用:

[...]特化を書くとき、その場所に関する注意が必要。または をコンパイルすると、 の自己イモラライドを起動するような試行になります。

はい、標準化委員会のメンバーも人間です。

+1

これはおそらく私が今までに受けた最高の答えだと思います!ありがとうございました。 –

関連する問題