Reputation: 1581
In the following code, I construct an instance of Node
by passing an instance of Point
. e.g. Node{{0, 1}};
However, the call to Node
's constructor is ambiguous. My guess it that {0, 1}
can also be comprehended as two int
representations of char
, and {char, char}
also applies to the constructor of std::string
, in the form of std::initializer_list<char>
.
I want to preserve the ability to construct Node
conviniently as Node{{0, 1}}
while also retain the conversion from std::string
to Node
, preferebly in the form of Node{std::string{"0,1"}}
or something like that. Is there a way to call the constructor that takes a string only if I specifies it to do so? or simply disable the std::string
's constructor of std::initializer_list<char>
?
Thanks in adavance.
#include <string>
class Point
{
public:
Point() {};
Point(int x, int y) : x(x), y(y) {}
private:
int x;
int y;
};
class Node
{
public:
Node(const std::string& str) {}
Node(const Point& dot) : dot(dot) {}
private:
Point dot;
};
int main()
{
Node{{0, 1}};
return 0;
}
Error Message:
/Users/cpp_sandbox.cpp:24:5: error: call to constructor of 'Node' is ambiguous
Node{{0, 1}};
^ ~~~~~~~~
/Users/cpp_sandbox.cpp:16:5: note: candidate constructor
Node(const std::string& str) {}
^
/Users/cpp_sandbox.cpp:17:5: note: candidate constructor
Node(const Point& dot) : dot(dot) {}
^
1 error generated.
[Finished in 0.1s with exit code 1]
[shell_cmd: g++ -std=c++11 "/Users/cpp_sandbox.cpp" -o "/Users/cpp_sandbox"]
[dir: /Users]
[path: /usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:/opt/X11/bin:/Applications/SquishCoco/:/Library/TeX/texbin]
Upvotes: 4
Views: 1526
Reputation: 56547
As mentioned in the comments, you're out of luck with your current code as both constructors are viable candidates. A solution is to use inheritance instead of aggregation and have Node
derive from Point
. You must also explicitly inherit Point
's constructor:
class Node: public Point
{
public:
using Point::Point; // constructor inheritance
Node(std::string str) {}
};
Now you can use
Node{0, 1}; // note that Node{{0, 1}} will invoke the string ctor
and
Node{"test"};
just fine.
Full example:
#include <iostream>
#include <string>
class Point
{
public:
Point() {};
Point(int x, int y) : x(x), y(y) {std::cout << __PRETTY_FUNCTION__ << '\n';}
private:
int x{};
int y{};
};
class Node: public Point
{
public:
using Point::Point;
Node(const std::string&) {std::cout << __PRETTY_FUNCTION__ << '\n';}
};
int main()
{
Node{0, 1};
Node{"test"};
}
If you wonder why now Node{{0, 1}}
will invoke the Node::Node(const std::string&)
constructor instead of an ambiguity, that's because the only viable constructor candidate is Node::Node(const std::string&)
(the other inherited constructor has the signature Point::Point(int, int)
, so it won't accept an initializer list). Whereas in your original code Node::Node(const Point&)
would also be a viable constructor due to the implicit conversion from an initializer list to a Point
(this kind of constructor is called a conversion constructor).
Upvotes: 4