问题
I'm writing a program under MS Visual C++ 6.0 (yes, I know it's ancient, no there's nothing I can do to upgrade). I'm seeing some behavior that I think is really weird. I have a class with two constructors defined like this:
class MyClass
{
public:
explicit MyClass(bool bAbsolute = true, bool bLocation = false) : m_bAbsolute(bAbsolute), m_bLocation(bLocation) { ; }
MyClass(const RWCString& strPath, bool bLocation = false);
private:
bool m_bAbsolute;
bool m_bLocation;
};
When I instantiate an instance of this class with this syntax: MyClass("blah")
, it calls the first constructor. As you can see, I added the explicit
keyword to it in the hopes that it wouldn't do that... no dice. It would appear to prefer the conversion from const char *
to bool
over the conversion to RWCString
, which has a copy constructor which takes a const char *
. Why does it do this? I would assume that given two possible choices like this, it would say it's ambiguous. What can I do to prevent it from doing this? If at all possible, I'd like to avoid having to explicitly cast the strPath
argument to an RWCString
, as it's going to be used with literals a lot and that's a lot of extra typing (plus a really easy mistake to make).
回答1:
Explicit will not help here as the constructor is not a part of the implicit conversion, just the recipient.
There's no way to control the preferred order of conversions, but you could add a second constructor that took a const char*. E.g:
class MyClass
{
public:
MyClass(bool bAbsolute = true, bool bLocation = false);
MyClass(const RWCString& strPath, bool bLocation = false);
MyClass(const char* strPath, bool bLocation = false);
private:
bool m_bAbsolute;
bool m_bLocation;
};
回答2:
Andrew Grant provided the solution. I want to tell you why it doesn't work the way you tried. If you have two viable functions for an argument, then the one that matches the argument best is called. The second requires a user-defined conversion, while the first only needs a standard conversion. That is why the compiler prefers the first over the second.
回答3:
If you don;t want to keep casting it, then it seems to me that you might have to make another ctor that takes a const char*.
That is what I would probably do in this situation.
(Not sure why you are making a ctor with a type that you aren't passing for most of its use.)
edit:
I see someone else already posted this while I was typing mine
回答4:
Not sure why it should confuse a reference to a string and a bool? I have seen problems with a bool and an int.
Can you lose the default value for the first constructor - it may be that since this is making it the default constructor for MyClass() then it is also the default if it can't match the args
回答5:
Your choices are to add a constructor that explicitly takes a const char *
MyClass(const char* strPath, bool bLocation = false); // Thanks Andrew Grant!
Or do
MyClass( string("blah") );
The compiler intrinsically knows how to make a const char * into a bool. It would have to go looking to see if, for any of the first-argument types of MyClass constructors, there's a constructor that will take the source type you've given it, or if it can cast it to a type that is accepted by any of the constructors of any of the first-argument types of your MyClass constructors, or... well, you see where this is going and that's only for the first argument. That way lies madness.
回答6:
The explicit
keyword tells the compiler it cannot convert a value of the argument's type into an object of your class implicitly, as in
struct C { explicit C( int i ): m_i(i) {}; int m_i; };
C c = 10;//disallowed
C c( 2.5 ); // allowed
C++ has a set of rules to determine what constructor is to be called in your case - I don't know from the back of my head but you can intuitively see that the default arguments lead towards ambiguity.
You need to think those defaults through.
You can fallback onto some static, named, construction methods. Or you can use a different class (which is not a bad choice from a design viewpoint). In either way, you let the client code decide which constructor to use.
struct C {
static C fromString( const char* s, const bool b = true );
static C fromBools( const bool abs = true, const bool b = true );
};
or
struct CBase {
bool absolute;
bool location;
CBase( bool abs=true, bool loc=true );
};
struct CBaseFromPath {
// makes 'absolute' true if path is absolute
CBaseFromPath( const char* s, const bool loc );
};
回答7:
Are you certain that it really is calling the first constructor? Are you calling it with the string hard-coded, or is it hidden behind a #define
? Are you sure that the #define is what you think it is? (Try compiling with the /Ef option to get the expanded preprocessor output, and see if the call looks like you'd expect it to look.)
EDIT: Based on this and other comments, my suggestion is to add another constructor for const char*.
Is that feasible?
来源:https://stackoverflow.com/questions/663724/wrong-argument-conversion-preferred-when-calling-function