3

はるかに大きなプロジェクトで問題を再現する次の最小限の例を検討してください。

スペック.h:

#include <iostream>

class A
{
public:
    template<typename T>
    T test(const std::string& a)
    {
        std::cout << "DEFAULT CALLED WITH " << a << "\n";
        return T();
    }
};

その他.cpp:

#include "spec.h"

template<>
float A::test<float>(const std::string& a)
{
    std::cout << "SPECIAL CALLED WITH " << a << "\n";
    return float();
}

spec.cpp:

#include <iostream>
#include "spec.h"

int main()
{
    A a;
    a.test<int>("int");
    a.test<float>("float");
    return 0;
}

コンパイル:

$ make
rm -f *.o lib.a output
clang++ -g other.cpp -c
clang++ -g spec.cpp -c
ar cr lib.a other.o
clang++ -g -o output lib.a spec.o
rm -f *.o output2
clang++ -g other.cpp -c
clang++ -g spec.cpp -c
clang++ -g -o output2 other.o spec.o

$ ./output
DEFAULT CALLED WITH int
DEFAULT CALLED WITH float

$ ./output2
DEFAULT CALLED WITH int
SPECIAL CALLED WITH float

質問:

なぜこうなった?なんとなく剥がれている?lib.a と直接オブジェクト ファイルの使用の違いは何ですか? :-)

ありがとう!

4

3 に答える 3

8

セクション 14.7.3p6 から:

テンプレート、メンバー テンプレート、またはクラス テンプレートのメンバーが明示的に特殊化されている場合、その特殊化は、暗黙的なインスタンス化を発生させる特殊化の最初の使用の前に、そのような使用が発生するすべての翻訳単位で宣言する必要があります。 ; 診断は必要ありません。プログラムが明示的な特殊化の定義を提供しておらず、特殊化が暗黙的なインスタンス化を引き起こすような方法で使用されているか、メンバーが仮想メンバー関数である場合、プログラムは不正な形式であり、診断は必要ありません。

その翻訳単位で最初に宣言せずに spec.cpp の特殊化を使用したため、プログラムの形式が正しくありません。または、次の段落が言うように:

関数テンプレート、クラス テンプレート、クラス テンプレートのメンバー関数、クラス テンプレートの静的データ メンバー、クラス テンプレートのメンバー クラス、クラス テンプレートのメンバー列挙、クラス テンプレートのメンバー クラス テンプレート、クラスのメンバー関数テンプレートの明示的な特殊化宣言の配置テンプレート、クラス テンプレートのメンバー テンプレートのメンバー関数、非テンプレート クラスのメンバー テンプレートのメンバー関数、クラス テンプレートのメンバー クラスのメンバー関数テンプレートなど、およびクラス テンプレートの部分的な特殊化宣言の配置、メンバー クラス テンプレートのメンバー クラス テンプレート非テンプレート クラス、クラス テンプレートのメンバ クラス テンプレートなど上と下で指定されているように、明示的な特殊化宣言の相対的な位置付けと翻訳単位でのそれらのインスタンス化のポイントに従って、プログラムが整形式であるかどうかに影響を与える可能性があります。

専門化を記述するときは、
その場所に注意してください。
またはそれをコンパイルすることは、その自己犠牲を燃やす
ような試練になるでしょう.

これは、標準全体で最も素晴らしい段落リメリックとして投票します。

于 2012-10-01T21:55:37.310 に答える
2

Ben Voigt の答えは正しいですが、少し追加したいと思います。

基本的に、2 つの異なるバージョンの関数を取得しています。1 つは other.o に、もう 1 つは spec.o (インライン テンプレートによって生成) にあります。リンカーは、標準が要求するように両方が同一であると仮定して、1 つだけを選択するように設計されています。最初のケースでは、シンボルがまだ定義されていない場合にのみ、リンカーはライブラリから定義を取得します。spec.o で定義されているため、ライブラリ定義は使用されません。

于 2012-10-01T21:59:52.617 に答える
1

ヘッダーの定義により、各翻訳単位は独自のインスタンス化を作成できます。したがって、特殊化されたバージョンを参照する未定義のシンボルは決してありません。同様に、ライブラリを参照すると、特殊化されたバージョンのオブジェクト ファイルは含まれていません。つまり、未定義のシンボルが定義されていません。リンク時にオブジェクト ファイルを明示的にインクルードする場合、リンカはそれをインクルードするしかありません。ただし、すべての特殊化を宣言する必要があります。宣言がないと、コンパイラは一般的なバージョンが適用できないという手がかりがありません。したがって、このバージョンが使用されるかどうかにかかわらず、シンボルがどのように扱われるかに依存します。

于 2012-10-01T21:56:50.623 に答える