C++ では、戻り値の型は関数シグネチャの一部と見なされますか? 戻り値の型を変更しただけでは、オーバーロードは許可されません。
4 に答える
通常の関数には、シグネチャに戻り値の型が含まれていません。
(注:この回答を書き直しました。以下のコメントはこのリビジョンには適用されません-詳細については、編集履歴を参照してください)。
序章
ただし、標準の関数と関数宣言に関する問題は複雑です。考慮する必要がある 2 つのレイヤーがあります。
- 宣言
- エンティティ
いわゆる関数宣言では、関数エンティティまたはテンプレート エンティティを宣言できます。関数エンティティが宣言されている場合は、(すべての引数を指定して) 関数テンプレートを明示的に特殊化するか、通常の関数を宣言する必要があります。テンプレート エンティティが宣言されている場合は、プライマリ関数テンプレート、またはいくつかの引数が指定されていない明示的な特殊化を宣言しています。(これは、「オブジェクト宣言」とオブジェクトまたは参照の関係に非常に似ています。前者は、オブジェクトまたは参照のいずれかを宣言できます。したがって、オブジェクト宣言は、必ずしもオブジェクトを宣言するとは限りません!)。
標準では、関数のシグネチャを次のように定義しています1.3.10
。
そのパラメーターの型、および関数がクラス メンバーの場合は、関数自体の cv- 修飾子 (存在する場合)、およびメンバー関数が宣言されているクラス。関数テンプレートの特殊化の署名には、そのテンプレート引数の型が含まれます。(14.5.5.1)
(最近の C++0x ワーキング ペーパーで指摘されているように、関数テンプレートの特殊化 (つまり、テンプレートの特殊化である関数を宣言する関数宣言) のシグネチャの一部であるこの定義には、戻り値の型がありません。14.5.5.1
すでに戻り値の型についても言及されていることを修正しました1.3.10
):
関数テンプレートの特殊化の署名は、関数テンプレートの署名と実際のテンプレート引数 (明示的に指定されているか推定されているかに関係なく) で構成されます。
関数テンプレートのシグネチャは、関数シグネチャ、戻り値の型、およびテンプレート パラメーター リストで構成されます。
では、署名には正確に何が含まれているのでしょうか?
したがって、関数の署名について尋ねると、次の2 つの答えが必要になります。
- 関数テンプレートの特殊化である関数の場合、シグネチャには戻り値の型が含まれます。
- 特殊化されていない関数の場合、戻り値の型はシグネチャの一部ではありません。
ただし、いずれの場合でも、戻り値の型は関数の型の重要な部分であることに注意してください。つまり、以下は無効です。
void f();
int (*pf)() = &f; // different types!
戻り値の型だけが異なる場合、オーバーロードが無効になるのはいつですか?
主要なコンパイラは現在、次のコードを拒否しています。
int f();
double f(); // invalid
ただし、次のコードを受け入れます。
template<typename T> int f();
template<typename T> double f(); // invalid?
ただし、標準では、戻り値の型のみが異なる関数宣言を禁止しています(オーバーロードが有効な場合と無効な場合を定義する場合)。ただし、「戻り値の型だけが異なる」とはどういう意味かを正確に定義しているわけではありません。
標準的な段落参照:
- 関数宣言をオーバーロードできるのは次の場合です。
13.1
- 関数宣言とは:
7/2
および7/5
- 関数テンプレート/特殊化の署名は何ですか:
14.5.5.1
参考までに、最新の C++0x ドラフト n3000 で の「署名」について述べ1.3.11
られている内容を以下に示します。これは、さまざまな種類のエンティティをカバーする点でより完全です。
関数の名前とパラメーター型リスト (8.3.5)、およびそれがメンバーであるクラスまたは名前空間。関数または関数テンプレートがクラス メンバーである場合、そのシグネチャには、関数または関数テンプレート自体の cv 修飾子 (存在する場合) および ref 修飾子 (存在する場合) が追加で含まれます。関数テンプレートのシグネチャには、戻り値の型とテンプレート パラメーター リストが追加で含まれます。関数テンプレートの特殊化の署名には、それが特殊化されているテンプレートの署名とそのテンプレート引数 (明示的に指定されているか推定されているかに関係なく) が含まれます。[ 注: 署名は、名前のマングリングとリンクの基礎として使用されます。— エンドノート]
関数が関数テンプレートであるかどうかによって異なります。
完全なガイドであるC++テンプレートでは、JusuttisはC ++標準で指定されているものとは異なる定義を提供しますが、同等の結果が得られます。
関数のシグネチャを次の情報として定義します。
- 関数の非修飾名
- その名前のクラスまたは名前空間のスコープ、および名前に内部リンケージがある場合は、名前が宣言されている変換単位
- 関数の
const
、、、volatile
またはconst volatile
資格 - 関数パラメーターのタイプ
- 関数が関数テンプレートから生成された場合、その戻り型
- 関数が関数テンプレートから生成される場合は、テンプレートパラメータとテンプレート引数
litbが示唆したように、return型がテンプレート関数のシグネチャの一部である理由を明確にすることは価値があります。
関数が異なるシグニチャを持っている場合、関数はプログラム内で共存できます。
。とはいえ、戻り型がテンプレートパラメータの場合:
template <typename T>
T foo(int a)
{return T();}
戻り値のタイプのみが異なる2つの関数をインスタンス化することは可能です。
foo<int>(0);
foo<char>(0);
だけでなく、litbによって正しく報告されているように、戻り型が依存名でなくても、戻り型のみが異なる2つのテンプレート関数をオーバーロードすることもできます。これが彼の例です:
template<class T> int foo(T)
{}
template<class T> bool foo(T)
{}
// at the instantiation point it is necessary to specify the cast
// in order not to face ambiguous overload
((int(*)(char))foo<char>)('a');
これらは、戻り値の型のみが異なる関数ポインター型に基づいて関数をオーバーロードできる型の十分な部分です。
int IntFunc() { return 0; }
char CharFunc() { return 0; }
void FuncFunc(int(*func)()) { cout << "int\n"; }
void FuncFunc(char(*func)()) { cout << "char\n"; }
int main()
{
FuncFunc(&IntFunc); // calls void FuncFunc(int_func func)
FuncFunc(&CharFunc); // calls void FuncFunc(char_func func)
}