4

1 つのメンバー関数を持つ単純なクラス テンプレートを定義しました。これは、追加の (明示的な) 特殊化を使用してクラスの外部で定義され、クラスの外部でも定義されます。すべてが 1 つのヘッダーファイルに含まれています。このヘッダーを複数の翻訳単位に含めると、One-Definition-Rule が原因でリンカー エラーが発生します。

// Header with a template

template <class T>
class TestClass
{
public:
    TestClass() {};
    ~TestClass() {};
    bool MemberFunction();
};

template <class T>
bool TestClass<T>::MemberFunction()
{
    return true;
}

template <>
bool TestClass<double>::MemberFunction()
{
    return true;
};

これまでのところすべて順調です。しかし、メンバー関数の定義をクラス本体内に配置すると、リンカー エラーがなくなり、関数をさまざまな翻訳単位で使用できるようになります。

// Header with a template

template <class T>
class TestClass
{
public:
    TestClass() {};
    ~TestClass() {};
    bool MemberFunction()
    {
        return true;
    }
};

template <>
bool TestClass<double>::MemberFunction()
{
    return true;
};

私の質問は、なぜそのように機能するのですか? 私は MSVC 2012 を使用しています。ODR には、私が最初に考えた理由であるテンプレートに関するいくつかの例外があります。しかし、クラスの内外の「ベース」関数の定義は、ここで違いを生みます。

4

1 に答える 1

5

14.7/5 言う

5 与えられたテンプレートと与えられた一連のテンプレート引数に対して、

  • 明示的なインスタンス化の定義は、プログラム内で最大 1 回出現するものとします。
  • 明示的な特殊化は、プログラム内で最大 1 回 (3.2 に従って) 定義する必要があります。
  • 明示的なインスタンス化と明示的な特殊化の宣言の両方が、明示的なインスタンス化が明示的な特殊化の宣言に続く場合を除き、プログラムに現れてはなりません。

このルールの違反を診断するために実装は必要ありません。

2 番目の箇条書きは、あなたのケースに適用されます。3.2 で定義された ODR は、あまり蒸留されていない形式ではありますが、同じことを述べています。

特殊化されていないバージョンのメンバー関数がどこでどのように定義されているかに関係なく、特殊化されたバージョンの定義は

template <> bool TestClass<double>::MemberFunction()
{
    return true;
};

ファイルに入る必要があり.cppます。ヘッダー ファイルに保持されている場合、ヘッダーが複数の翻訳単位に含まれると、ODR 違反が発生します。GCC は、この違反を確実に検出します。その点で、MSVC は信頼性が低いようです。しかし、上記の引用が述べているように、このルールの違反を診断するために実装は必要ありません。

ヘッダー ファイルには、その特殊化の非定義宣言のみを含める必要があります。

template <> bool TestClass<double>::MemberFunction();

MSVC では、特殊化されていないバージョンの関数がどのように定義されているかなど、一見無関係な要因に応じてエラーが発生したり消えたりするという事実は、MSVC コンパイラの癖に違いありません。


さらに調査した結果、MSVC の実装は実際には壊れているようです。その動作は、言語仕様によって与えられた「診断は必要ありません」許可によって許可されているものを超えています。

実験で観察した動作は、次のことと一致しています。プライマリ関数テンプレートを宣言するinlineと、そのテンプレートの明示的な特殊化も自動的にinline行われます。これはそうあるべきではありません。14.7.3/14 では、言語仕様は次のように述べています

関数テンプレートの明示的な特殊化は、関数テンプレートがインラインであるかどうかに関係なく、インライン指定子で宣言されているか、削除されていると定義されている場合にのみインラインです。

于 2014-10-10T18:11:06.413 に答える