I want to make a "Tag" class that can have its name specified either as a dot separated name like "this.is.my.name" or as a vector of strings, like {"this","is","my","name"}.
When I try to do this, I am sometimes getting told by the compiler that my calls are ambiguous. I want to know (1) why this is ambiguous at all, and (2) why it is only ambiguous sometimes.
Here is my example code, which you can also view and compile here on Coliru
#include <string>
#include <vector>
#include <iostream>
class Tag
{
public:
explicit Tag(std::string name);
explicit Tag(std::vector<std::string> name);
};
Tag::Tag(std::string name)
{
//here 'name' will be a dotted collection of strings, like "a.b.c"
}
Tag::Tag(std::vector<std::string> name)
{
//here 'name' will be a vector of strings, like {"a","b","c"}
}
int main(int argc, char**argv)
{
Tag imaTag{{"dotted","string","again"}};
Tag imaTagToo{"dotted.string"};
//everything is fine without this line:
Tag imaTagAlso{{"dotted","string"}};
std::cout << "I made two tags" << std::endl;
}
With the indicated line, I get the following error:
g++ -std=c++11 -O2 -Wall -pthread main.cpp && ./a.out
main.cpp: In function 'int main(int, char**)':
main.cpp:28:39: error: call of overloaded 'Tag(<brace-enclosed initializer list>)' is ambiguous
Tag imaTagAlso{{"dotted","string"}};
^
main.cpp:18:1: note: candidate: 'Tag::Tag(std::vector<std::__cxx11::basic_string<char> >)'
Tag::Tag(std::vector<std::string> name)
^~~
main.cpp:13:1: note: candidate: 'Tag::Tag(std::__cxx11::string)'
Tag::Tag(std::string name)
^~~
Tag imaTagAlso{{"dotted","string"}};says construct aTag, call itimaTagAlsoand initialize it with{"dotted","string"}. The issue with that isstd::stringcan be constructed by a pair of iterators and since string literals can decay toconst char*'s, they qualify as iterators. So you could either call the string constructor using the "iterators", or you could call the vector constructor using itsstd::initializer_listconstructor. To work around this you can usewhich says construct a
Tag, call itimaTagAlsoand initialize it with{{"dotted"},{"string"}}and now{"dotted"}and{"string"}become elements of thestd::initializer_listfor the vector constructor.You could also (since c++14) use
std::string's user defined literal operator (""s) likewhich makes each element of the braced-init-list
std::string's, and the vector constructor will be chosen.