14

バックグラウンド

C ++ 03では、テンプレート引数として使用されるシンボルには外部リンケージが必要です。この前の質問で検討したように、この制限はC++11で削除されました。

C ++ 03では、テンプレート引数に内部リンクを設定できませんでした。

[C++03: 14.6.4.2/1]:テンプレートパラメータに依存する関数呼び出しの場合、関数名が非修飾IDであるが、テンプレートIDではない場合、候補関数は、次の場合を除いて、通常のルックアップルール(3.4.1、3.4.2)を使用して検出されます。

  • 非修飾名ルックアップ(3.4.1)を使用したルックアップの部分では、テンプレート定義コンテキストからの外部リンケージを持つ関数宣言のみが見つかります。
  • 関連する名前空間(3.4.2)を使用したルックアップの一部では、テンプレート定義コンテキストまたはテンプレートインスタンス化コンテキストのいずれかで見つかった外部リンケージを持つ関数宣言のみが見つかります。

[..]

これは、C ++ 11で変更されました(問題#561:「依存名ルックアップの内部リンケージ関数」 ):

[C++11: C.2.6]:14.6.4.2
変更:内部リンケージを使用して関数の依存呼び出しを許可する理由
過度に制約され、過負荷解決ルールを簡素化します。

その結果:

[C++11: 14.6.4.2/1]:テンプレートパラメータに依存する関数呼び出しの場合、候補関数は、次の点を除いて、通常のルックアップルール(3.4.1、3.4.2、3.4.3)を使用して検出されます。

  • 非修飾名ルックアップ(3.4.1)または修飾名ルックアップ(3.4.3)を使用したルックアップの一部では、テンプレート定義コンテキストからの関数宣言のみが検出されます。
  • 関連する名前空間(3.4.2)を使用したルックアップの一部では、テンプレート定義コンテキストまたはテンプレートインスタンス化コンテキストのいずれかで見つかった関数宣言のみが見つかります。

[..]

(不足している「外部リンケージ付き」の資格を見つけます。)

問題#561(「依存名ルックアップの内部リンケージ関数」)、C ++ 11で制限が削除されることになった提案は、次のように尋ねます。

さらに、ルックアップから内部リンケージ関数を除外する必要が本当にありますか?ODRは、名前検索に別の問題を発生させることなく、このケースを処理するのに十分な許容範囲を実装に提供しませんか?

後の答えで:

グループのコンセンサスは、[..]内部リンケージ関数はルックアップによって検出される必要があるというものでした(ただし、過負荷解決によって選択された場合、エラーが発生する可能性があります)。


質問

制限の元々の実際的な理由は何でしたか?

元の標準的な表現は、外部リンケージを持つシンボルへのルックアップを制限するために邪魔になったので、1つあったに違いないようです。

「[内部リンケージ関数]が過負荷解決によって選択された場合、エラーが発生する可能性がある」というだけで、2000年代を通じて、これがどれほど重要であるかについて意見が変わりました。または、おそらく別のC ++ 11機能の別の場所での新しい表現の間接的な結果として、何か他の変更がありましたか?

4

2 に答える 2

8

exportC++98の悪名高いテンプレート機能に関連しているのではないかと思います。考えてみてください。テンプレート定義が別々の翻訳単位で表示される可能性を許可したが、テンプレート引数が指定されるまで(つまり、テンプレートがインスタンス化されるまで)真にコンパイルできない場合は、このトワイライトゾーンに入り、テンプレートの定義を含むTUとインスタンス化されたTUは、オーバーロード解決の観点からコンテキストを共有しながら、リンカーの可視性ルール(つまり、分離モデル)に従わなければなりません。この問題の解決策は、依存する名前のルックアップで外部リンケージを持つ関数のみを許可することです。

これが例です。エクスポートされたテンプレートのあまり知られていない「機能」の1つは、テンプレートのTUに、内部リンケージ(つまり、マークさstaticれた名前空間または名前のない名前空間)を持ついくつかの関数またはクラスを含めることができることです。インスタンス化されたTUに内部リンケージ機能もある場合はどうなりますか?テンプレートのTU内のTUとあいまいであるか、場合によってはそれに取って代わられる可能性があります。それは少しシュールな問題です、私は知っています、それはエクスポートされたテンプレートの奇妙な世界です。非常に驚くべき動作を回避する唯一の方法は、ルックアップからすべての内部リンケージ関数を除外することです。また、エクスポートされたテンプレートを実際に実装する方法を誰も明確に理解しておらず、この制限なしで実装することはおそらくさらに不可能に思われることも考慮してください。

したがって、エクスポートされたテンプレートが公開されると、依存する名前の検索に対する制限は明らかに役に立たないように思われ、あまり議論されることなく削除されました。少なくとも、それは私には完全に理にかなっていますが、もちろん、それは推測です。

具体的な例を次に示します。

// in exptemp.h
export template <typename T> bool is_valid(T value);

// in exptemp.cpp
namespace {
  bool is_space(char c) {
    return (c == ' ') || (c == '\t');
  };
};

template <typename T>
bool is_valid(T value) {
  return is_space(value);
};

// in test.cpp
#include "exptemp.h"

namespace {
  bool is_space(char c) {
    return (c == ' ') || (c == '\t') || (c == '\n');
  };
};

int main() {
  char c = '\n';
  return is_valid(c);   // will this return 0 or 1 ?!?!?
};
于 2013-01-30T06:51:45.177 に答える
3

私の知る限り、これは純粋に歴史的なものです。cfrontの名前マングリングでは正しく処理できなかったため、当初は禁止されていたようです。

ある時点で、Anthony Williamsは、許可されることを提案し、その方法を説明する論文を書きましたが、AFAIKは、その論文が受け入れられたことも、その要件が標準に編集されたこともありません。それは他の何よりもタイミングの問題だと思います。2001年に提案されたので、当時(C ++ 2003)に取り組んでいたものは、あまり新しい素材を追加することを意図したものではなく、C ++ 11に本格的に取り組み始めた頃には、ほとんどがそうだったようです。忘れてしまった。

于 2013-01-30T06:24:50.367 に答える