1

そして、私が解決できない別のテンプレートの特殊化の問題:

ターミナルログ.hh

//stripped code

class Terminallog {
public:

    Terminallog();
    Terminallog(int);
    virtual ~Terminallog();

    template <class T>
    Terminallog & operator<<(const T &v);
    template <class T>
    Terminallog & operator<<(const std::vector<T> &v);
    template <class T>
    Terminallog & operator<<(const T v[]);
    Terminallog & operator<<(const char v[]);

    //stripped code 
};

terminallog.hh 続き (コメントのおかげで編集)

//stripped code 

template <class T>
Terminallog &Terminallog::operator<<(const T &v) {
    std::cout << std::endl;
    this->indent();
    std::cout << v;
    return *this;
}

template <class T>
Terminallog &Terminallog::operator<<(const std::vector<T> &v) {
    for (unsigned int i = 0; i < v.size(); i++) {
        std::cout << std::endl;
        this->indent();
        std::cout << "Element " << i << ": " << v.at(i);
    }
    return *this;
}

template <class T>
Terminallog &Terminallog::operator<<(const T v[]) {
    unsigned int elements = sizeof (v) / sizeof (v[0]);
    for (unsigned int i = 0; i < elements; i++) {
        std::cout << std::endl;
        this->indent();
        std::cout << "Element " << i << ": " << v[i];
    }
    return *this;
}

inline
Terminallog &Terminallog::operator<<(const char v[]) {
    std::cout << std::endl;
    this->indent();
    std::cout << v;
    return *this;
}

//stripped code 

これは問題なくコンパイルされ、エラーは発生しません。ただし、次のようなことをしようとすると:

Terminallog clog(3);
int test[] = {5,6,7,8};
clog << test;

配列のポインターアドレスを常に出力します。つまり、特殊なテンプレート

Terminallog & operator<<(const T v[]);

呼び出されることはありません。また、追加のカウントでこれを確認しました。何を試しても、プログラムは常に呼び出しています

Terminallog & operator<<(const T &v);

専門ではありません。明らかに、コードにエラーがあるはずですが、見つかりません。

4

7 に答える 7

3

私の賭けは、ここで変換規則が適用されるということです。(配列の実際の型) に完全に一致するものがないためint [5]、配列は に減衰しint*、オーバーロードconst T&が選択const T v[]されconst T* vます。

この場合のオーバーロード解決メカニズムの詳細な説明については、@sth の回答を参照してください。

試してみるとどうなりますか:

template <class T, size_t n>
Terminallog & operator<<(const T (&v)[n]);

代わりは ?

ところで、sizeofwith のオーバーロードの定義はT[]明らかに間違っています。この方法ではサイズを取得できません。ここでも、配列はポインターに減衰し、elements常にsizeof(T*) / sizeof(T).

于 2011-03-28T12:10:43.587 に答える
3

あなたのコードでは、いくつかのオーバーロードされた関数テンプレートを定義します (それらはいくつかの一般的なテンプレートの特殊化ではなく、別個のオーバーロードです。しかし、それには何の問題もありません。それらが特殊化されなければならない理由はありません。)

これらのテンプレートの 1 つに、 のパラメータ宣言がありconst T v[]ます。配列は値で渡すことができないため、これはパラメーターが宣言されている場合とまったく同じようにコンパイラーによって解釈されますconst T *v

最終的に type になる問題の配列の場合int[5]、コンパイラは 2 つの一致するテンプレートから選択する必要があります。最適な一致は、標準の §13.3.3.1.1 (表 10) に従って、必要な変換の数と種類によって決まります。

  • const T&テンプレートは に一致しますT = int[5]。§13.3.3.1.4/2 によるとint[5]、 を constパラメーターに変換するには、 を const にint(&)[5]変換するのと同じ変換が必要です。これは 1 つの修飾変換です ( を追加します)。int[5]int[5]const
  • const T*テンプレートは に一致しますT = intint[5]aを a に変換するには、 const int*2 つの変換が必要です。最初に配列からポインタへの変換 ( int[5]to int*)、次に修飾変換 ( int*to const int*)。

これらの変換はすべて「完全一致」と見なされますが、2 番目のテンプレートではそのような変換が 2 回必要であるのに対し、最初のテンプレートでは 1 回しか必要ないため、最初のテンプレートの方が一致度が高くなります。

「正しい」一致を取得するにconstは、2 番目のテンプレートのパラメーターから を削除するか、const バージョンを呼び出すだけの非 const ポインター用のテンプレートを追加します。

template <class T>
Terminallog& Terminallog::operator<<(T *v) {
   *this << static_cast<const T*>(v);
}

sizeofとはいえ、このような関数では配列の長さを取得できないことに注意してください。Alexandre C. が彼の回答で提案したような追加のサイズ パラメータを持つテンプレートは、そのためのより良い選択かもしれません。

于 2011-03-28T13:20:54.897 に答える
2

まず第一に、ここには特殊化はありませんが、オーバーロードされた関数があります。

次に、問題は次のように仮定します。

int test[] = {5,6,7,8}; // <-- this guy is "decayed" to int* in next call
clog << test;

したがって、オーバーロードの解決中に、コンパイラーは次のいずれかを選択します

template <class T>
Terminallog & operator<<(const T &v);

template <class T>
Terminallog & operator<<(const T v[]);

最初のものは完全一致なので、「勝ち」です。

于 2011-03-28T12:16:25.380 に答える
1

まず第一に、テンプレートの特殊化を行っていません:

template <class T>
Terminallog & operator<<(const T v[]);
Terminallog & operator<<(const char v[]);

2つの異なる機能です。T型の出力を定義しようとした場合char、コンパイラはあいまいさを訴えるはずです. テンプレートを特化していることを示すには、ssteinberg が指摘しているように、template<>表記法を使用する必要があります。

ただし、この場合、メンバー関数を特殊化できるとは思わないため、これはおそらく役に立ちません (静的な場合はできるでしょうか?)。したがって、ssteinberg のアドバイスに従おうとすると、コンパイラは文句を言うでしょう。クラス全体をテンプレート化してから、個々の関数を特化する必要があります。

次のリンクは、いくつかの助けを提供するかもしれません

編集:

以下は実例となる可能性があります。

#include <vector>
#include <iostream>

template<class T>
class Terminallog { 
public:

  Terminallog(){};
  Terminallog(int){};
  virtual ~Terminallog(){};

  //general vector output: will be specialized for vectors of chars
  Terminallog & 
  operator<<(const std::vector<T> &v);

  //general reference output: will be specialized for chars
  Terminallog & operator<<(const T &v);

  //general pointer output: will be specialised for char pointers
  Terminallog & operator<<(const T* v);

  //stripped code 
};

//general code for reference type
 template <class T>
 Terminallog<T>&
 Terminallog<T>::operator<<(const T &v) {
   std::cout<<"This general reference"<<std::endl;
     return *this;
 }

//specialisation for chars reference
template <>  //as noted by ssteinberg
Terminallog<char>&
Terminallog<char>::operator<<(const char &v) {
  std::cout<<"This is for chars"<<std::endl;
  return *this;
}

//general code for pointer type
 template <class T>
 Terminallog<T>&
 Terminallog<T>::operator<<(const T* v) {
   std::cout<<"This general pointers"<<std::endl;
     return *this;
 }

//specialisation for chars pointer
//as noted by alexandre your array will decay to this....
template <>  
Terminallog<char>&
Terminallog<char>::operator<<(const char* v) {
  std::cout<<"This is for chars pointers"<<std::endl;
  return *this;
}


//Non specialised vector
template <class T>
Terminallog<T>&
Terminallog<T>::operator<<(const std::vector<T> &v) {
  std::cout<<"This general vector"<<std::endl;
  return *this;
}

//specialisation for  vector of chars
template <>
Terminallog<char>&
Terminallog<char>::operator<<(const std::vector<char> &v) {
  std::cout<<"This is a vector of chars"<<std::endl;
  return *this;
}

int
main  (int ac, char **av)
{
  Terminallog<int> ilog(3);
  int testint[] = {5,6,7,8};
  std::vector<int> testvi;
  testvi.push_back(1);
  testvi.push_back(3);
  testvi.push_back(5);

  Terminallog<char> clog(3);
  char testchar[] = {5,6,7,8};
  std::vector<char> testvc;
  testvc.push_back(1);
  testvc.push_back(3);
  testvc.push_back(5);

  ilog << testint;
  ilog << testvi;
  clog << testchar;
  clog << testvc;


}

出力は

This general pointers
This general vector
This is for chars pointers
This is a vector of chars
于 2011-03-28T11:54:39.017 に答える
1

まず、extern テンプレートのようなものはありません (C++ 標準には export キーワードがありましたが、MS や GNU などの主要なコンパイラ メーカーによって無視され、現在は放棄されているようです)。したがって、テンプレート関数本体をヘッダー ファイルに配置する必要があります。

2 つ目: 部分的なテンプレートの特殊化は忘れたほうがよいでしょう。これは十分にサポートされていません。たとえば、MS は部分的なクラス テンプレートの特殊化 (ポインター、参照、メンバーへのポインター、および関数ポインター(こちらを参照) ) に対して非常に限定的なサポートしか提供していません。だから使わないほうがいい。ただし、完全に明示的なテンプレートの特殊化を使用できます。

3 番目: コードにテンプレートの特殊化がまったくない

template <class T>
Terminallog & operator<<(const T &v);
template <class T>
Terminallog & operator<<(const std::vector<T> &v);
template <class T>
Terminallog & operator<<(const T v[]);

は 3 つの異なる関数テンプレートであり、

Terminallog & operator<<(const char v[]);

単なる機能です。

関数テンプレートの特殊化の正しい構文は次のとおりです

template <class T>
Terminallog& out(const T& v)
{
// default implementation
}

template <class T>
Terminallog& out< std::vector<T> >(const std::vector<T>& v)
{
// partially specialized implementation
}

template <>
Terminallog& out<double>(const double& v)
{
// fully specialized implementation
}

しかし、それは本当のポイントではありません。オーバーロードの解決は、標準で定義された規則に従って、最も特殊化された関数または関数テンプレート (そのような関数が存在しない場合) につながる必要があります。しかし、完全に準拠した C++ 実装が存在するかどうかはわかりません (誰も使用しない標準の作成者によって開発された Comeau C++ を除く)。2 つのオーバーロードが完全に一致するか、まったく一致しない場合 (そして暗黙的な変換が必要な場合) は、非準拠の問題が発生する可能性があると思います。

また、注意:

関数テンプレートの特殊化は、ネームスペース スコープでのみ許可されます。これは、メンバー関数テンプレートの特殊化を宣言できないことを意味します。しかしもちろん、あなたがしたようにオーバーロードを定義することもできます。

于 2011-03-28T12:36:40.713 に答える
0

テンプレートを特殊化していることをコンパイラに伝えるためにtemplate<>、前に配置してみてください。Terminallog & operator<<(const char v[]);

于 2011-03-28T11:33:08.360 に答える
0

明示的な特殊化されたテンプレートのインスタンス化:

inline template<>
Terminallog & operator<<(const char v[]);

このようなもの。私のC++は錆びています。

于 2011-03-28T11:36:01.583 に答える