mkanakis
mkanakis

Reputation: 383

Wrong constructor is being called

I have a class which creates different kinds of variables.

And through some macro defines we want to initialize those variables.

#define NUMBER(number)      JsonVariable(number)
#define STRING(text)        JsonVariable(text)
#define TRUE                JsonVariable(true)
#define FALSE               JsonVariable(false)

Numbers are initialized fine, but Strings call the Bool constructor for unknown reasons.

JsonVariable(string x)
    : type(String), s(x)
{
    cout << "String" << x << endl;
    sleep
}
JsonVariable(bool x)
    : type(Boolean), b(x)
{
    cout << "Boolean" << x << endl;
    sleep
}

If I comment out the Bool constructor then the String one is called.

Any suggestions?

EDIT: This is a string constructor with the defined macros. std::string is used in the constructor.

JSON(test) = STRING("Hello")

Type is a defined enum. Also macros must be used as part of this assignment.

EDIT2: For clarification. Here's the enum type.

std::string is used with namespace std and thus single string. Also String is from the enum type so

String != string

typedef enum {
    Null, Integer, Double, Boolean, String, Object, Array
} datatype;

Upvotes: 1

Views: 895

Answers (2)

lorro
lorro

Reputation: 10880

You might experience the effect of conversion rules. If you pass a const char*, bool overload is preferred over std::string&:

void f(std::string&) { std::cout << "string"; }
void f(bool) { std::cout << "bool"; }

int main() {
    f("abc"); // prints 'bool'
}

This is logical: pointers are usually checked for being non-zero, thus conversion to int / bool types is desirable. On the other hand, binding to a const std::string& means a std::string constructor and taking reference. Binding to std::string& is impossible here as temporaries don't bind to non-const references.

Workaround is either to manually create a string or, preferably, to have a constructor for const char*.

Upvotes: 3

WhiZTiM
WhiZTiM

Reputation: 21576

As a rule of thumb, whenever your API exposes overloaded functions or constructors of this signature (or its CV qualified and/or reference variants):

void func(std::string){}
void func(bool){}

You should always provide the one for const char*,

void func(std::string){}
void func(bool){}
void func(const char* c){ func(std::string(c)); }

else you and your users (especially) may be in for subtle suprises because:

string-literals (e.g "abc...") decays quickly to const char* and by overload resolution, conversion to bool has a higher precedence than a user-defined conversion which is what std::string does.

Upvotes: 2

Related Questions