6

テンプレートのインスタンス化 [*] を使用すると、次の問題が発生します。

ファイルfoo.h

class Foo
{
public:
    template <typename F>
    void func(F f)

private:
    int member_;
};

ファイルfoo.cc

template <typename F>
Foo::func(F f)
{
     f(member_);
}

ファイルcaller.cc

Foo::func(boost::bind(&Bar::bar_func, bar_instance, _1));

これは正常にコンパイルされますが、リンカーは未定義のシンボルについて不平を言います:

void Foo::func<boost::_bi::bind_t...>

関数 をインスタンス化するにはどうすればよいFoo::funcですか? 引数として関数を取るので、少し混乱しています。通常の非関数型に慣れているので、インスタンス化関数をfoo.ccに追加しようとしました。

instantiate()
{
    template<> void Foo::func<boost::function<void(int)> >(boost::function<void(int)>);
}

明らかに、これは機能しません。誰かが私を正しい方向に向けることができれば幸いです。

ありがとう!

[*] はい、parashift FAQ lite を読みました。

4

5 に答える 5

5

これに対する答えはコンパイラに依存します。一部のバージョンのSunC++コンパイラは、個別の変換ユニット間で共有されるテンプレート関数実装のキャッシュを構築することにより、これを自動的に処理します。

Visual C ++、およびこれを実行できないその他のコンパイラを使用している場合は、関数定義をヘッダーに配置することもできます。

ヘッダーが複数の.ccファイルに含まれている場合は、定義の重複について心配する必要はありません。コンパイラーは、テンプレートで生成されたメソッドに特別な属性を付けて、リンカーが文句を言う代わりに重複を破棄することを認識できるようにします。これが、C++に「単一定義規則」がある理由の1つです。

編集:上記のコメントは、テンプレートが任意のタイプパラメータを指定してリンクできる必要がある一般的な場合に適用されます。クライアントが使用するタイプの閉集合がわかっている場合は、テンプレートの実装ファイルで明示的なインスタンス化を使用して、それらが使用可能であることを確認できます。これにより、コンパイラーは、リンクする他のファイルの定義を生成します。ただし、テンプレートがクライアントにしか知られていないタイプで動作する必要がある一般的なケースでは、テンプレートをヘッダーファイルと実装ファイルに分離する意味はほとんどありません。とにかく、クライアントは両方の部分を含める必要があります。クライアントを複雑な依存関係から分離する場合は、それらの依存関係をテンプレート化されていない関数の背後に隠してから、テンプレートコードからそれらを呼び出します。

于 2008-10-23T00:41:54.663 に答える
3

必要に応じてファイルに分割します
。これはお勧めしません。それが可能であることを示しているだけです。

plop.h

#include <iostream>
class Foo
{
public:
    Foo(): member_(15){}


    // Note No definition of this in a header file.
    // It is defined in plop.cpp and a single instantiation forced
    // Without actually using it.
    template <typename F>
    void func(F f);

private:
    int member_;
};


struct Bar
{
     void bar_func(int val) { std::cout << val << "\n"; }
};

struct Tar
{
    void tar_func(int val) { std::cout << "This should not print because of specialisation of func\n";}
};

プロップ.cpp

#include "plop.h"
#include <boost/bind.hpp>
#include <iostream>

template <typename F>
void Foo::func(F f)
{
     f(member_);
}

// Gnarly typedef
typedef boost::_bi::bind_t<void, boost::_mfi::mf1<void, Bar, int>, boost::_bi::list2<boost::_bi::value<Bar>, boost::arg<1> (*)()> > myFunc;

// Force the compiler to generate an instantiation of Foo::func()
template void Foo::func<myFunc>(myFunc f);

// Note this is not a specialization as that requires the <> after template.
// See main.cpp for an example of specialization.

main.cpp

#include "plop.h"
#include <boost/bind.hpp>
#include <iostream>

// Gnarly typedef
typedef boost::_bi::bind_t<void, boost::_mfi::mf1<void, Tar, int>, boost::_bi::list2<boost::_bi::value<Tar>, boost::arg<1> (*)()> > myTar;

// Specialization of Foo::func()
template<> void Foo::func<myTar>(myTar f)
{
    std::cout << "Special\n";
}
// Note. This is not instantiated unless it is used.
// But because it is used in main() we get a version.

int main(int argc,char* argv[])
{
    Foo f;
    Bar b;
    Tar t;

    f.func(boost::bind(&Bar::bar_func, b, _1)); // Uses instantiation from plop.cpp
    f.func(boost::bind(&Tar::tar_func, t, _1)); // Uses local specialization
}
于 2008-10-23T04:49:43.743 に答える
1

foo.cc を caller.cc に含めていますか。インスタンス化は、コンパイル時に発生するものです。コンパイラが呼び出し元で呼び出しを確認すると、テンプレートのインスタンス化されたバージョンが作成されますが、完全な定義が利用可能である必要があります。

于 2008-10-23T00:35:41.027 に答える
1

どちらも参照しているのは、テンプレート関数の定義 (宣言だけでなく) を、それらが使用されるファイルに含める必要があるということです。テンプレート関数は、使用されない限り/使用されるまで実際には存在しません。#includeそれらを別のccファイルに入れると、パーサーの動作方法により、そのccファイルをヘッダーファイルまたはそれらを呼び出しているファイルに明示的に指定しない限り、コンパイラーは他のccファイルでそれらについて認識しません.

(そのため、Earwicker が説明したように、テンプレート関数の定義は通常、ヘッダー ファイルに保持されます。)

より明確ですか?

于 2008-10-23T02:42:24.653 に答える
0

Earwicker が正しいと思います。この場合、テンプレート メンバー関数 func を明示的にインスタンス化する際の問題は、boost::bind によって返される型が実装に依存することです。これはboost::関数ではありません。boost::functionには、右側の型 (boost::bind の結果) を推測するテンプレート代入演算子があるため、boost::bindを含めることができます。caller.cc でのこの特定の func の使用では、boost のこの特定の実装で、boost::bind のタイプは実際には < と > の間のリンカ エラーで言及されているタイプです (つまりboost::_bi::bind_t...)。しかし、その型の func を明示的にインスタンス化すると、おそらく移植性の問題が発生します。

于 2008-10-23T02:44:50.577 に答える