コンパイラによるdoSomething
の最初の2つの呼び出しはなぜ正常なのに、リスト内の2つの要素を使用するとあいまいな呼び出しが発生するのですか?
#include <vector>
#include <string>
void doSomething(const std::vector<std::string>& data) {}
void doSomething(const std::vector<int>& data) {}
int main(int argc, char *argv[])
{
doSomething({"hello"}); // OK
doSomething({"hello", "stack", "overflow"}); // OK
doSomething({"hello", "stack"}); // C2668 'doSomething': ambiguous call
return 0;
}
ここで何が起こっているかというと、2つの要素の初期化リストでは、両方の文字列リテラルがconst char*
であるため、暗黙的にconst char[N]
に変換できるということです。現在、std::vector
には、ポインターが修飾する2つのイテレーターをとるコンストラクターがあります。そのため、initializer_list
のstd::vector<std::string>
コンストラクターは、std::vector<int>
のイテレーター範囲コンストラクターと競合しています。
代わりにコードを変更すると
doSomething({"hello"s, "stack"s});
次に、初期化リストの要素はstd::string
sになり、あいまいさはなくなりました。
引数が1つのリストと引数が3つのリストはどちらも、std::vector<std::string>
のstd::initializer_list
コンストラクターにのみ一致できます。ただし、2つの引数のリストは、std::vector<int>
のコンストラクターの1つと一致します。
template <class InputIt>
vector(InputIt first, InputIt last, Allocator const &alloc = Allocator());
実際、char const *
をインクリメントして逆参照すると、暗黙的にchar
に変換できるint
を取得できます。
"hello"
と"stack"
はどちらもconst char *
に減衰します。これは InputIterator の概念を満たしています。これにより、それらを一致させることができます std::vector
のコンストラクター#4 。
std::string
オブジェクトを渡すと、あいまいさが解消されます。