7

小さなスタンドアロンプ​​ロジェクトで使用されるクラス Foo があります。Foo.h にクラス定義があり、実装ファイル Foo.cpp にクラスのメンバー関数が実装されています。

最初の質問 - クラス Foo のメンバー関数の 1 つはテンプレート メソッド Foo::doSomething() です。このメソッドの実装が Foo.h の関数の宣言と共に表示されるのは正しいですか?

Foo::doSomething() がインスタンス化されるテンプレート パラメータは、クラス CalcA と CalcB の 2 つの Functor タイプの 1 つです。

するべきか:

  • (A) 2 つの Functor クラスの定義と実装をすべて Foo.cpp にまとめます (実際には、他の Foo メンバー関数の実装によって Foo::doSomething を呼び出すために使用されます)。
  • (B) 2 つの Functor クラスの定義と実装を Foo.h に配置します。
  • (C) 通常のクラスで行うように、2 つのファンクターの定義と実装を Foo.h と Foo.cpp に分割する必要がありますか?
4

4 に答える 4

8

まず、テンプレートのメカニズムを理解する必要があります。テンプレートはコンパイルされません。テンプレートは使用時にインスタンス化され、その後インスタンス化がコンパイルされます。したがって、コンパイラは、渡されたパラメーターに従って最初にインスタンス化するために、テンプレート関数を使用して各モジュールに完全なテンプレート定義を含める必要があります。

問題を解決するには 3 つの解決策がありますが、どちらも同じ結果になることがわかります。クラス定義内のヘッダー ファイルにテンプレート全体を実装します (テンプレート定義が含まれていることを正確に示すために、.h の代わりに .hxx をサフィックスとして使用します)。

// Foo.hxx

#ifndef __FOO_HXX__
#define __FOO_HXX__

class Foo {
  public:
   template <class T>    
   void bar(const T& t) {
      t.doSomething();
   }
 };
#endif

または、クラスから定義を外部化することもできますが、ヘッダー ファイル内に残します。

// Foo.hxx

#ifndef __FOO_HXX__
#define __FOO_HXX__

class Foo {
    public:
       template <class T>    
       void bar(const T&);
};

template <class T>
void Foo::bar(const T& t) {
   t.doSomething();
}
#endif

最後に、テンプレート メソッド本体を外部ファイルに実装できます (同じ理由で .cxx のプレフィックスが付いています)。メソッドの本体は含まれますが、「Foo.hxx」は含まれません。代わりに、クラス定義の後に「Foo.cxx」をインクルードするのは「Foo.hxx」です。このようにして、コンパイラは #include ディレクティブを解決するときに、同じモジュール内のテンプレート定義全体を見つけ、それをインスタンス化できるようにします。

// Foo.hxx

#ifndef __FOO_HXX__
#define __FOO_HXX__
class Foo {
    public:
       template <class T>    
       void bar(const T&);
};

#include "Foo.cxx"

#endif

// Foo.cxx
template <class T>
void Foo::bar(const T& t) {
   t.doSomething();
}

テンプレートを実装するこれら 3 つの方法の選択は、むしろ読みやすさ (および好み) の問題です。
2 番目と 3 番目は、生成されたコードの点では同等ですが、インクルードを反転するのを忘れると愚かなエラーが発生することが多いため、cxx ファイル ソリューションは使用しません。

さらに、STL や Boost などのよく知られた C++ ライブラリは、ヘッダー ファイルのみでコードを提案します。これは、優れた設計の兆候です。ヘッダー内で外部定義を使用することにより、クラスの定義を明確にします。また、Herb Sutter http://www.gotw.ca/gotw/033.htmによると、コンパイラがメソッドを自動的にインライン化するのを防ぎます。これにより、結果が悪くなることがあります。

于 2010-11-30T17:11:07.863 に答える
7

原則:

foo::doSomething() が foo.cpp の外部で使用される場合 (つまり、通常は public または protected である場合)、ヘッダーに入れる必要があります。

そうでない場合は、cpp ファイルを挿入してもまったく問題ありません (ヘッダー ファイルが煩雑にならないようにするため)。

そのため、ファンクターが cpp ファイルでのみ使用されている場合は、必ずテンプレート関数もそこに配置してください。これが変更された場合は、後でいつでもリファクタリングできます。

于 2010-11-30T16:29:51.043 に答える
1

テンプレート メソッドの定義は、それが属するクラスのヘッダー ファイルにある必要があります。

このような:

class MyClass
{
    template <typename T>
    void foo(const T&)
    {
        // Definition
    }
};

または、このように (テンプレート メソッド定義は、クラス宣言の後に別のファイルから含めることができることに注意してください)

class MyClass
{
    template <typename T> void foo(const T&);
};

template <typename T>
void MyClass::foo(const T&)
{
    // Definition
}

残りは、同意したスタイルとニーズによって異なります。

Foo だけでなく、Foo がそれらをクラスメンバーとして持っている場合は、ファンクター宣言 (単純な場合は定義も) をヘッダーに入れます。

于 2010-11-30T16:32:14.733 に答える
1

私のデフォルトは、次のように、メンバー関数テンプレートの定義を .h ファイルに配置することです。

class Foo
{
public: 
  template<typename T> void DoSomething(T t);
};

// ... later...

template<typename T>
void Foo::DoSomething(T t)
{
  // ...
}

これが特定のケースで次善の策である場合、私はより大胆な手段を講じます。.h ファイルの末尾にある定義を使用して .inc ファイルを -ing することから始める#includeか、メンバー関数テンプレートを使用する必要がある .cpp ファイルで明示的なインスタンス化を行うことさえあります。

于 2010-11-30T16:29:35.273 に答える