4

テンプレート コードを提供するライブラリを提供したいと思います。ただし、テンプレートのさまざまな通常のタイプの使用法を推測できる場合は、このコード (生成されたコード) の所有権を可能な限り維持したいと考えています。これが私がやろうとしていることの例です:

lib1.h

#include <iostream>

template<int N>
void print_me() {
    std::cout << "I am function number " << N << std::endl;
}

lib1.cpp

#include "lib1.h"

/* Force symbols to be defined here. */
template void print_me<0>();
template void print_me<1>();

以下を使用してライブラリをコンパイルします。

g++ -共有 -fPIC lib1.cpp -o lib1.so

そして、ライブラリを使用する場合:

main.cpp

#include <lib1.h>

int main() {
    print_me<0>();
    print_me<1>();
    print_me<2>();
}

以下でコンパイル:

g++ main.cpp -l1

ここで、シンボル print_me<0>() および print_me<1>() が定義され、lib1.so から使用され、print_me<2>() が定義され、実行可能ファイルに使用されることが期待されます ( nm --defined-only でチェック) )。しかし、そうではないようです!0 と 1 のシンボルは lib1.so で明確に定義されていますが、弱いシンボルとして定義されています。そして、私の実行可能ファイル(0、1、および2)で再び再定義されますが、弱いです。これは、実行可能ファイルの 0 と 1 のコードが main.cpp から取得されたものであることを意味しますが、これは必要なものではありません (main.cpp の仕様で確認しました)。

シンボルが既にどこかで定義されており、これらのシンボルを追加する必要がないことを main.cpp のコンパイル時に (たとえば lib1.h で) 言う方法はありますか?

4

3 に答える 3

3

C++11 ソリューション: extern テンプレートを使用します。main.cppこれらの文字列をファイルに追加するだけです。

extern template void print_me<0>();
extern template void print_me<1>();

print_meしたがって、関数テンプレートをインスタンス化しないようにコンパイラに指示しますmain.cpp(テンプレート引数 0 および 1 の場合)。そのため、リンカは と の定義を他の翻訳単位で検索する必要がvoid print_me<0>();ありvoid print_me<1>();ます。

于 2014-02-01T12:00:08.797 に答える
3

テンプレートの特殊化を使用して、実装とインターフェイスを分離します。

lib1.h:

#include <iostream>
template <int T> void print_me(void);
template <> void print_me<0>(void);
template <> void print_me<1>(void);

lib1_internal.h (注: これは開示する必要はありません):

#include <iostream>

template<int N>
void print_me_internal() {
    std::cout << "I am function number " << N << std::endl;
}

lib1.cpp:

#include "lib1.h"
#include "lib1_internal.h"

template <> void print_me<0>() {
  print_me_internal<0>();
}
template <> void print_me<1>() {
  print_me_internal<1>();
}

あなたの main.cpp は正しくリンカ エラーにつながります:

$ g++ main.cpp -L. -l1
/tmp/ccZSDqkp.o: In function `main':
main.cpp:(.text+0xf): undefined reference to `void print_me<2>()'
collect2: ld returned 1 exit status

template <int T> void print_me(void)宣言の代わりにinの定義を追加するだけlib1.hで、特殊化されたバージョンで見つからない場合はいつでも使用されます。

于 2014-01-31T15:15:30.407 に答える
2

ヘッダー ファイルで実装を非表示にする必要があります。

//lib1.h    
template<int N>
void print_me();

//lib1.cpp
#include <iostream>
#include "lib1.h"
template <int printme>
void print_me()
{
  std::cout << printme << std::endl;
}

template void print_me<0>();
template void print_me<1>();

何が起こっているかというと、テンプレートが通常どのように使用されるかです: テンプレートは必要な場合にのみ構築されるため (それ以外の場合はすべての整数に対して print_me を構築する必要があります)、ソフトウェアの実行時に実装を把握します (これがコンパイル時間を遅くする理由です)。そのため、テンプレートを使用するコンパイル単位ごとにクラスを再構築します)。テンプレートの定義がある限り、必要な任意の引数に対してテンプレートを構築できます。これは、.cpp ファイルで定義を非表示にすることがある程度有効である数少ないケースの 1 つですが、エンド ユーザーは通常、print_me<2>() に対して無駄なエラーを 1 回受け取ることになります。このフレームワーク(C++ 11)を使用する方が良いでしょう(imho):

//lib1.h
#include <type_traits>
template<int N>
typename std::enable_if<N == 1 || N == 0>::type print_me();

//lib1.cpp
#include <iostream>
#include "lib1.h"

template<int N>
typename std::enable_if<N == 1 || N == 0>::type print_me()
{
  std::cout << N << std::endl;
}

template void print_me<0>();
template void print_me<1>();

現在、エラーは「print_me() の呼び出しに一致する関数がありません」であり、これは少し改善されています... static_assert を使用してネストされた関数を実行して、さらに改善することができます。

于 2014-01-31T14:49:13.323 に答える