まず、コンパイラは の左右の型を調べます<<
。std::cout
は 型std::ostream
で、文字列リテラルは15 の配列const char
型です。左はクラス型なので、という名前の関数を検索しますoperator<<
。問題は、それがどこに見えるかです。
この名前のルックアップoperator<<
は、関数名が のように修飾されていないため、いわゆる非修飾ルックアップstd::operator<<
です。関数名の非修飾ルックアップは、引数依存のルックアップを呼び出します。引数依存の検索では、引数の型に関連付けられたクラスと名前空間が検索されます。
を含める<iostream>
と、署名の自由な機能
template<typename traits>
std::basic_ostream<char, traits>& operator<<(std::basic_ostream<char, traits>&,
const char*);
は namespace で宣言されていますstd
。この名前空間は の型に関連付けられているstd::cout
ため、この関数が見つかります。
std::ostream
は単なる typedef でstd::basic_ostream<char, std::char_traits<char>>
あり、15 の配列はconst char
char const*
(配列の最初の要素を指す) に暗黙的に変換できます。したがって、この関数は 2 つの引数型で呼び出すことができます。
のオーバーロードは他にもありますがoperator<<
、上記の関数は、引数の型とこの場合に選択されたものに最適です。
引数依存ルックアップの簡単な例:
namespace my_namespace
{
struct X {};
void find_me(X) {}
}
int main()
{
my_namespace::X x;
find_me(x); // finds my_namespace::find_me because of the argument type
}
注: この関数は演算子であるため、実際のルックアップはもう少し複雑です。これは、最初の引数 (クラス型の場合) のスコープ内で修飾ルックアップを介して、つまりメンバー関数としてルックアップされます。さらに、修飾されていないルックアップが実行されますが、すべてのメンバー関数が無視されます。非修飾ルックアップは実際には 2 段階の手順に似ており、引数依存のルックアップが 2 番目のステップであるため、結果はわずかに異なります。最初のステップでメンバ関数が見つかった場合、2 番目のステップは実行されません。つまり、引数依存のルックアップは使用されません。
比較:
namespace my_namespace
{
struct X
{
void find_me(X, int) {}
void search();
};
void find_me(X, double) {}
void X::search() {
find_me(*this, 2.5); // only finds X::find_me(int)
// pure unqualified lookup (1st step) finds the member function
// argument-dependent lookup is not performed
}
}
に:
namespace my_namespace
{
struct X
{
void operator<<(int) {}
void search();
};
void operator<<(X, double) {}
void X::search() {
*this << 2.5; // find both because both steps are always performed
// and overload resolution selects the free function
}
}