3

重複の可能性:
「template」および「typename」キーワードをどこに、なぜ配置する必要があるのですか?

私は(しなければならなかった:))数週間前にC ++開発者になりました(以前はいくつかの経験がありましたが、あまり多くはありませんでした.Javaの方が多かったです)、重要なことすべてを学び、できるだけ効率的に開発しようとしています。私の質問がまったくばかげている場合はすみません。簡単なテンプレート クラスの例に問題があります。

template<typename T>
class SameCounter {
private:
    map<T,int> counted;
public:
    SameCounter(list<T> setup) {
        for(list<T>::iterator it = setup.begin(); it != setup.end(); it++) {
            counted[*it]++;
        }
    }
    map<T,int>::const_iterator& begin() { // line 25
        return counted.begin();
    }
    map<T,int>::const_iterator& end() {
        return counted.end();
    }
};

...
// using the class
Reader rdr;
rdr.Read();
SameCounter<char> sc(rdr.GetData());

コンパイル中にエラーが発生します:

Error   3   error C4430: missing type specifier - int assumed. Note: C++ does not support default-int   d:\learn_cpp\examples\gyakorlas_1.cpp   25
Error   2   error C2143: syntax error : missing ';' before '&'  d:\learn_cpp\examples\gyakorlas_vizsga\gyakorlas_1.cpp  25

(both of them twice)

私はそれについての手がかりを持っていません.SameCounterを通常のクラスとして作成すれば、それはまったく問題ないので、おそらく私が仮定するテンプレートに何か問題があります. お手伝いありがとう。

4

2 に答える 2

9

これはあなたを助けるはずです:

typename map<T,int>::const_iterator& begin() {
    return counted.begin();
}
typename map<T,int>::const_iterator& end() {
    return counted.end();
}

C++ テンプレートは注意が必要です。T はテンプレート パラメーターであり、map<T, int>::const_iterator渡す T に応じて、異なるもの (型名だけでなく、たとえば静的メンバーなど) を意味する可能性があります。

そのため、テンプレートでは、意図を明確にし、実際に「const_iteratorは型であり、それへの参照が必要です」という意味であることを示す必要がある場合があります。キーワード「typename」はそれを可能にします。

参照: http://pages.cs.wisc.edu/~driscoll/typename.html


コードを単純化し、 の必要性を減らしないtypenameようにするには、次から始めることができます。

private:
    typedef std::map<T, int> MapType;
    MapType counted;

そして、ただ一緒に行きます

typename MapType::const_iterator &begin() {

残念ながら、これtypenameはまだここにある必要があります。typedef typename依存型ごとに、それをさらに宣言から削除する必要があります(@ rhalbersmaの回答を参照)。


@ rhalbersmaのコメントに続いて、これらの反復子を値で返す必要があることも強調させてください。一時オブジェクトへの参照を返すと、オブジェクトがスコープ外になり、「ダングリング参照」になってしまうため、未定義の動作が発生します。

だからそれを作る:

typename MapType::const_iterator begin() {
于 2013-01-10T09:10:43.730 に答える
2

以下にあなたのクラスに注釈を付けました。言及する価値のあるいくつかのポイント:

  template<typename T>
  class SameCounter 
  {
  private:
     typedef map<T,int> MapType; // typedef here to keep specific container in a single place
     typedef typename MapType::const_iterator const_iterator; // to avoid retyping "typename"
     typedef typename MapType::iterator iterator; // to avoid retyping typename
     MapType counted;
  public:
     SameCounter(list<T> setup) {
        // auto here to avoid complicated expression
         for(auto it = setup.begin(); it != setup.end(); it++) {
             counted[*it]++;
         }
     }

    // by value instead of by reference, mark as const member
    const_iterator begin() const {
        return counted.begin();
    }

    // by value instead of by reference, mark as const member
    const_iterator end() const {
        return counted.end();
    }

    // probably best to also forward cbegin()/cend() and non-const begin() / end()
 };
  • 内部typedefmapは、別のコンテナ(たとえば)に変更したい場合に便利であり、ネストされたtypedefunorderd_mapの繰り返し入力を回避します。typename
  • auto(C ++ 11キーワード)は、複雑なイテレータタイプの入力を制限できます
  • イテレータの値による戻りは慣用的な方法です
  • const- begin()/ end()の正当性
  • 非定数イテレータのbegin()/ end()をオーバーロードし、cbegin()/ cend()も提供します

一般に、ラップしている関数(この場合はマップのbegin()/ end())と同じインターフェース(定数、戻り値)を使用するのが最善です。

于 2013-01-10T09:17:34.687 に答える