3

注:いくつかの関連する質問 (たとえば、この質問) は、この質問の重複としてマークされました。私はこの特定の質問を認識しており、対応する回答の解決策に従います。ただし、コンパイラが異なれば動作も異なり、その理由はわかりません。

私のライブラリはクラス テンプレートを備えており、ライブラリ内の特定のテンプレート引数のインスタンスを提供したいと考えています。これは、テンプレートがかなりのコンパイル時間を必要とするためです。クラス テンプレートは次のようになります ( stack.hpp)

#ifndef MY_STACK
#define MY_STACK

template<class T>
class stack
{
public:
    stack();
};

#endif

その実装は対応するstack.tppファイルに存在します

#ifndef MY_STACK_TPP
#define MY_STACK_TPP

#include <iostream>

template<class T>
stack<T>::stack()
{
    std::cout << "My stack constructor!" << std::endl;
}

#endif

特定のテンプレート引数のみをサポートしたいのでstack.cpp、次の明示的なテンプレート インスタンスを作成します。

#include "stack.hpp"

template class stack<double>;
template class stack<char>;

#include "stack.tpp"

これは g++ および clang++ でコンパイルされますが、結果の共有ライブラリのシンボルには違いがあります。

g++ -std=c++11 -c stack.cpp -o stack.so
nm -C stack.so | grep stack
0000000000000049 t _GLOBAL__sub_I_stack.cpp
0000000000000000 W stack<char>::stack()
0000000000000000 W stack<char>::stack()
0000000000000000 n stack<char>::stack()
0000000000000000 W stack<double>::stack()
0000000000000000 W stack<double>::stack()
0000000000000000 n stack<double>::stack()

対。

clang++-7 -std=c++11 -c stack.cpp -o stack.so
nm -C stack.so | grep stack
0000000000000050 t _GLOBAL__sub_I_stack.cpp

私のアプリケーションでは、そのような明示的にインスタンス化されたクラスのコンストラクターは、clang++ では見つかりませんが、g++ では問題なく動作します。この基本的な MWE が理由を示していると思います。clang++ を使用してクラス テンプレートのコンストラクター シンボルを取得する方法を誰か教えてもらえますか?

4

1 に答える 1

4

このプログラムは整形式です

[temp.explicit]/1 の引用 [私の強調]:

クラス、関数、変数、またはメンバー テンプレートの特殊化は、そのテンプレートから明示的にインスタンス化できます。クラス テンプレートのメンバー関数、メンバー クラス、または静的データ メンバーは、そのクラス テンプレートに関連付けられたメンバー定義から明示的にインスタンス化できます。[..]

そして、[temp.explicit]/9 [強調鉱山]を引用します。

クラス テンプレートの特殊化に名前を付ける明示的なインスタンス化定義は、クラス テンプレートの特殊化を明示的にインスタンス化し、インスタンス化の時点で定義されているメンバーのみの明示的なインスタンス化定義です

したがって、OP の例では、 の明示的stack<T>なインスタンス化定義はコンストラクターの明示的なインスタンス化定義を含みません。これは、 の明示的なインスタンス化定義が、インクルードを介してコンストラクターの定義を提供する前にstack<T>配置されるためです。.tpp

引用[temp.point]/8 [強調鉱山]:

関数テンプレート、メンバー関数テンプレート、またはクラス テンプレートのメンバー関数または静的データ メンバーの特殊化は、翻訳単位内にインスタンス化の複数のポイントを持つことができます。翻訳単位内にインスタンス化のポイントを持つ特殊化では、翻訳単位の最後もインスタンス化のポイントと見なされます。クラス テンプレートの特殊化は、翻訳単位内に最大 1 つのインスタンス化ポイントを持ちます。テンプレートの特殊化には、複数の翻訳単位でインスタンス化のポイントがある場合があります。インスタンス化の 2 つの異なるポイントが、1 つの定義規則に従ってテンプレートの特殊化に異なる意味を与える場合、プログラムは形式が正しくなく、診断は必要ありません。

したがって、stack.cppがインスタンス化の 2 つの異なるポイントを含み、1 つが のインクルードの前でもう 1 つがインクルードの後に​​あるstack.tpp場合、プログラムは不適切な形式です。

ただし、インスタンス化のポイントは、クラス テンプレートとそのメンバー関数 (/constructor) の使用方法に依存するため、ここでは少し注意が必要です。上で引用した[temp.explicit]/9でカバーされているように、 の明示的なインスタンス化stack<T>は、そのコンストラクターの明示的なインスタンス化の定義にはなりません。代わりに、詳細、特に条項 1 について[temp.point]にフォールバックする必要があります。 2 および 4 で、その使用コンテキストが を含める前にインスタンス化ポイントにつながる場合stack.tpp

問題のスタンドアロンの例は、これらのケースのいずれにも含まれていないため、プログラムの形式は正しくありません

GCC vs clang:一見異なるインスタンス化の動作?

clang++ を使用してクラス テンプレートのコンストラクター シンボルを取得する方法を誰か教えてもらえますか?

コンストラクターは決して使用されないため、インスタンス化する必要はまったくありませんが、(OP シンボル ダンプから) GCC はとにかくそうするように見えますが (これは違法ではありません)、clang はそうではありません。を含めた後にコンストラクターを使用/参照する場所がある場合track.tpp、GCC と clang の両方が自然にそれをインスタンス化します (使用される特定の特殊化のために)。

于 2020-02-14T12:28:26.800 に答える