web-dev-qa-db-ja.com

戻り型は関数シグネチャの一部ですか?

C++では、戻り値の型は関数シグネチャの一部と見なされますか?戻り値の型を変更しただけではオーバーロードは許可されません。

65
yesraaj

通常の関数は、署名に戻り値の型を含めません。

note:この回答を書き直しましたが、以下のコメントはこのリビジョンには適用されません-詳細については、編集履歴を参照してください)。

前書き

ただし、標準の関数と関数宣言に関する問題は複雑です。考慮しなければならない2つの層があります。

  • 宣言
  • エンティティ

いわゆる関数宣言は、関数エンティティまたはテンプレートエンティティを宣言できます。関数エンティティが宣言されている場合、関数テンプレートの明示的な特殊化(すべての引数を指定)、または通常の関数の宣言のいずれかを行う必要があります。テンプレートエンティティが宣言されている場合、プライマリ関数テンプレート、またはいくつかの引数が指定されていない明示的な特殊化を宣言しています。 (これは、「オブジェクト宣言」とオブジェクトまたは参照の関係に非常に似ています。前者はオブジェクトまたは参照のいずれかを宣言できます。そのため、オブジェクト宣言必ずしもオブジェクトを宣言するとは限りません!)。

標準では、1.3.10に次のものを含めるように関数のシグネチャを定義しています。

パラメータの型、および関数がクラスメンバーの場合、関数自体とメンバー関数が宣言されているクラスのcv修飾子(存在する場合)。関数テンプレートの特殊化の署名には、そのテンプレート引数のタイプが含まれます。 (14.5.5.1)

この定義には戻り値の型がありません。これは、関数テンプレート特殊化の署名の一部です(つまり、関数を宣言する関数宣言テンプレートの特殊化)、14.5.5.1(最近のC++ 0xワーキングペーパーで修正され、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++テンプレート-完全なガイドでは、JustuttisはC++標準で定義されている定義とは異なる定義を提供しますが、結果は同等です。

関数のシグネチャを次の情報として定義します。

  1. 関数の非修飾名
  2. その名前のclassまたはnamespaceスコープ、および名前に内部リンケージ、名前が宣言されている翻訳単位
  3. constvolatile、またはconst volatile関数の資格
  4. 関数パラメーターのtypes
  5. 関数が関数テンプレートから生成される場合、その戻り値type
  6. template parametersおよびtemplate arguments(関数が関数テンプレート

litbが示唆するように、戻り値の型がテンプレート関数のシグネチャの一部である理由を明確にする価値があります。

関数は、別個の署名を持っている場合、プログラム内で共存できます。

。つまり、戻り値の型がテンプレートパラメーターの場合:

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'); 
10
Nicola Bonelli

これらは、戻り値の型のみが異なる関数ポインター型に基づいて関数をオーバーロードできる型の一部です。

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)
}
2
Eclipse