2016-05-22 6 views
23

以下の定義があります。C++でのテンプレート関数のオーバーロード

using namespace std; 

template <typename T> 
void foo(const T &s) { 
    cout << 1; 
} 

template <typename T> 
void foo(const T *s) { 
    cout << 2; 
} 

int main(int argc, const char * argv[]) { 
    char str[] = "ss"; 
    char *s = str; 
    foo(s); 

    return 0; 
} 

そして、それは私の理解から

1 

を出力し、両方のバージョンは、constの変換を通過する必要があります。その後、void foo(const T *s)はより特殊化され、呼び出される必要があります。コンパイラはvoid foo(const T& s)を選択しました。説明は何ですか?

+2

'のconst T&S'> 'するchar * constの&' –

+1

私はそれをコンパイルすることだし、まだそれが選ぶ信じることができません1 – bolov

答えて

15

一部の人々は、コンパイラここ

void f(char * const&) 
void f(const char *); 

によって選ばれたとして、テンプレートのパラメータは、最初の関数のために、char*、コンパイラはcharへのポインタを期待していることに気づくと、それはconst参照だと指摘しています。あなたがあなたのケースのために、それは最初のテンプレートを好むことが判明した場合には、驚きとして来るかもしれませんが、以下の二つのために、は、それが第二

template <typename T> 
void foo(const T& s) { 
    cout << 1; 
} 

template <typename T> 
void foo(T &s) { 
    cout << 2; 
} 

ので、もちろん、それはます外観を好むだろう時々constで。なぜあなたのケースでそれをしなかったのですか?他の関数にも参照がある場合は、参照用のconstのみを参照するためです。

char*からconst char*まではポインタの変換ですが、lvalueからconst lvalueまでは実際には変換されません。 const参照によるconstの追加は、両方の関数が上記のような参照パラメータを持つ場合を除いて、オーバーロード解決によって無視されます。

+1

私が以前に「左辺値変換」についての答えに書いたことは、ナンセンスです。私はこの答えを書いたとき、私は半分眠っていました。 –

+0

BTWの前にhttp://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1401という便利なメモがありました。「リファレンスとの互換性がある式への参照のバインディング追加された修飾は、標準変換のランクに影響します; 13.3.3.2 [over.ics.rank]と8.5.3 [dcl.init.ref]を参照してください。 "彼らがこの文章を守り、言い直せば、私は好きだろう。 「標準的な変換のランクに影響を与える」という表現は、ランダムではなく、単なるランク付けに影響することを強調し、それ自体でコンバージョンを構成するものではありません。 –

5

これは、sが非定数ポインタであるため、int型const &は実際にはint型const *よりもよく一致します。なぜなら、ポインタ型にconstを追加する必要がないからです。

sがconst修飾された場合、T const *バージョンと完全に一致します。

2

両方のバージョンがconst変換を経なければなりません。

第1のものは変換する必要はありません。テンプレート引数char *を持つテンプレートパラメータconst T&の場合、Tchar*と推定され、関数パラメータのタイプはchar* const &となるため、完全に一致します。関数の引数はconst(T - >const T&、つまりchar*char* const&)への参照にバインドされます。const修飾は変換ではありません。テンプレート引数char *と第二1、テンプレートパラメータconst T*については

Tcharと推測され、その後、関数パラメータのタイプはconst char*なり、qualification conversionconst char*char*を変換するために必要とされます。

どちらのルーチンがsに低レベルのconstを追加

あなたのコメントから。

第一1はsconstを追加されていますが、第二1ではありません。 sが指しているものにconstを追加しています。sではなくこれが違いです。

+0

argment 'char *'の 'char * const&'にはconst変換も必要であると予想できました。直観的には、それほど遠く離れていなくても、IMOです。 –

+1

なぜ 'const T&s'は' T = char * 'で' char * const& 'に行くのですか?私は 'const'の位置が変わる理由を意味します。 –

+0

@ JohannesSchaub-litbはい、それは私が期待していたものです。どちらのルーチンも、低レベルのconstを 's'に追加します。しかし、なぜこの変換はここでは起こりませんでしたか? –

4

コードの意味を変更せずにconstを移動しました。ちょうどconstの位置が後で「変更する」と驚くことはありません。

template <typename T> 
void foo(T const &s) { 
    cout << 1; 
} 

template <typename T> 
void foo(T const *s) { 
    cout << 2; 
} 

char *x = "x"; 
foo(x); 

過負荷1はそうsのタイプはchar * const &(非const charへのconstポインタを参照)であろうchar*ことがTを推測しなければなりません。そのような参照は、変換なしで引数型(char *;非const charへのポインタ)にバインドできます。

過負荷2はそうsのタイプはchar const *charをCONSTへのポインタ)になりcharことがTを推測しなければなりません。これにより、引数タイプ(char *;非constへのポインタchar)からパラメータタイプ(char const *; const charへのポインタ)への修飾変換が発生します。

過1における原理のさらに例示的な例は以下の通りである:

int n = 42; 
int const &i = n; 

それが資格を追加基準だから非constエンティティへのconst参照を結合は、変換を必要としませんそれを参照型と照合するためにエンティティに追加される資格ではありません。

1

引数がポインタであるとき、あなたはポインタで渡す必要があるため理由は次のように..です:

// Example program 
#include <iostream> 
#include <string> 

using std::cout; 
using std::endl; 

void foo(int *a) 
{ 
    cout << *a << endl; 
} 

int main() 
{ 
    int a = 5; 
    foo(&a); 
} 

しかし、あなたの引数がrefrenceであればそのままあなただけのパラメータを渡すことができますこのように: - > `Tのconstの&S` - >` T = CHAR * ` -

// Example program 
#include <iostream> 
#include <string> 

using std::cout; 
using std::endl; 

void foo(int &a) 
{ 
    cout << a << endl; 
} 

int main() 
{ 
    int a = 5; 
    foo(a); 
} 
関連する問題