Implicit conversion from char to int for constructors in C++ - c++

class A {
int x;
std::string s;
public:
A(std::string _s): x(0), s(_s) {
std::cout << "\tA(string)\n" ;
}
A(int _x): x(_x), s("") {
std::cout << "\tA(int)\n" ;
}
A(const A &other): x(other.x), s(other.s) {
std::cout << "\tA(A& other)\n" ;
}
};
int main() {
std::string str = "Hello";
A obj_1(str);
A obj_2 = str;
A obj_3(10);
A obj_4 = 10;
char ch = 'a';
A obj_5 = ch; // How is this working?
// A obj_6 = "Hello"; // And not this?
const char *ptr = "Hello";
// A obj_7 = ptr; // or this?
return 0;
}
On executing this code, the output is:
A(string)
A(string)
A(int)
A(int)
A(int)
As far as I understand, obj_1 and obj_3 are created directly by using the respective ctors. For obj_2 and obj_4, the compiler does an implicit conversion by calling their respective ctor (so only 1 implicit conversion is required in each case). However, for obj_5 the compiler first has to convert from char to int and then one more implicit conversion to call the int-ctor. But C++ standard allows only 1 implicit conversion. So what is happening here?
Also, in that case "Hello" should first get converted to a std::string then one more implicit conversion to call the string-ctor. But this doesn't work.

But C++ standard allows only 1 implicit conversion.
Thats not correct.
From cppreference:
Implicit conversion sequence consists of the following, in this order:
zero or one standard conversion sequence;
zero or one user-defined conversion;
zero or one standard conversion sequence.
From the language point of view, const char[N] -> std::string (or const char* to std::string) is a user-defined conversion. Hence, the commented out lines are errors. On the other hand,
A obj_5 = ch; // How is this working?
is fine, because there is only a single user-defined conversion involved.

Related

What is the best way to prevent implicit conversion of integer 0 to pointer in c++

I am trying to figure out the best way to prevent integer 0 from being implicitly cast to nullptr_t and then passed to constructors that take pointers. Explicit doesn't do it, but I can get nullptr_t to cause an ambiguous overload error:
#include <typeinfo.h>
struct A {
explicit A(char*) { }
};
struct B {
B(nullptr_t a) = delete;
B(char*) { }
};
int main(int argc, char* argv[])
{
A a(0); // darnit I compiled...
B b1(0); // good, fails, but with only b/c ambiguous
B b2((char*)0); // good, succeeds
B b3(1); // good, fails, with correct error
}
Is there a better way than this? Also, what exactly is delete accomplishing here?
Deleting A(int) will not prevent A(char *) being called with
nullptr. So you will need to delete A(nullptr_t) as well.
And even this will not protect you from the eventuality that
there is some rogue class in the neighbourhood that is
implicitly constructible from 0 or nullptr and
implicitly converts same to char *.
#include <iostream>
struct A {
A(int) = delete;
A(std::nullptr_t) = delete;
explicit A(char * p) {
std::cout << "Constructed with p = " << (void *)p << std::endl;
}
};
struct X
{
X(long i) : _i(i) {}
operator char *() const { return (char *)_i; }
long _i;
};
int main()
{
X x(0);
A a(x);
return 0;
}
The program prints:
Constructed with p = 0
You may reckon that possibility remote enough to be ignored;
or you might prefer to delete all constructors,
in one fell swoop, whose argument is not precisely of a type
that you sanction. E.g. assuming the sanctioned types are just char *:
struct A {
template<typename T>
A(T) = delete;
explicit A(char * p) {}
};
struct X
{
X(long i) : _i(i) {}
operator char *() const { return (char *)_i; }
long _i;
};
int main()
{
// A a0(0);
//A a1(nullptr);
X x(0);
// A a2(x);
char *p = x;
A a3(p); // OK
return 0;
}
Here all of the commented out constructor calls fail to compile.
If you want to stop your constructor from taking 0, one option would be to delete B(int):
B(int) = delete;
That's an unambiguously better match for B(0) than the constructor that takes a char *.
Note that up until C++11, NULL was specified as being of integer type, and even in C++11 and later, it's still likely implemented as #define NULL 0. B(NULL) won't work if you do this. B(nullptr) will work, but still, I'd be wary of whether this is worth doing.

Function matching in vector constructors

#include <string>
#include <iostream>
#include <vector>
class HasPtrValue {
public:
HasPtrValue(const HasPtrValue& h): ps(new std::string(*h.ps)) { std::cout << "copy" << std::endl;}
HasPtrValue(const std::string &s = std::string()): ps(new std::string(s)) { std::cout << "string/default" << std::endl;}
~HasPtrValue() { delete ps; }
private:
std::string *ps;
};
using namespace std;
int main(){
string s = "stackoverflow";
vector<HasPtrValue> a(5, s);
}
The above code compiles fine outputting:
string/default
copy
copy
copy
copy
copy
This suggests to me the vector first directly initialises a temporary HasPtrValue object using the string object (doing HasPtrValue(s))and then copy-constructs the 5 elements from this temporary. How come, then, the following code does not compile:
int main(){
vector<HasPtrValue> a(5, "stackoverflow");
}
If it was directly initialising the HasPtrValue (doing HasPtrValue("stackoverflow")) then there would be no issue in the const string& constructor taking up the role of creating the temporary. I get the error;
error: no matching function for call to 'std::vector<HasPtrValue>::vector(int, const char [14])'|
I thought I'd try it with a simpler class that uses an int constructor and converts from a double:
class A{
public:
A(const int& a): x(a) { }
int x = 2;
};
int main(){
vector<A> a(5, 5.5);
}
Except this compiles fine. What part of the vector implementation prevents using a const char* conversion in the constructor?
Because it needs two user defined conversions, const char* -> std::string, and then std::string -> HasPtrValue, but only once user defined implicit conversion is permitted in an implicit conversion sequence.
13.3.3.1.2$1 User-defined conversion sequences [over.ics.user]
A user-defined conversion sequence consists of an initial standard
conversion sequence followed by a userdefined conversion (12.3)
followed by a second standard conversion sequence.
Note here only one level of user defined implicit conversion is legal. For your case this must be handled by explicit conversions; So you can:
vector<HasPtrValue> a(5, std::string("stackoverflow"));
int main(){
vector<HasPtrValue> a(5, string("stackoverflow"));
}
Your constructor requires std::string and "stackoverflow" is char array.
Alternatively you can define additional constructor accepting char[].

Implicit construction of std::string does not happen during copy-initialization

I'm attempting to copy-initialize my CObj class as follows in the main() function:
#include <string>
#include <iostream>
class CObj
{
public:
CObj(std::string const& str) : m_str(str) { std::cout << "constructor" << std::endl; }
~CObj() { std::cout << "destructor" << std::endl; }
private:
std::string m_str;
};
int main()
{
CObj obj = "hello";
std::cout << "done" << std::endl;
}
However, the line CObj obj = "hello" fails to compile even though std::string is implicitly constructible from a char const*. According to my understanding here, this should work. Any reason why it doesn't? If I do this it works:
CObj obj = std::string("hello");
The literal "Hello" has type const char[6] : in order to call your constructor, two conversions are required : one to std::string and a second one to CObj .
But C++ only allows one user-defined conversion when doing an implicit conversion :
C++ Standard section ยง 12.3/4 [class.conv]
Type conversions of class objects can be specified by constructors and by conversion functions. These conversions are called user-defined conversions and are used for implicit type conversions
[...]
At most one user-defined conversion (constructor or conversion function) is implicitly applied to a single value.
This is why this works :
CObj obj = std::string("hello");
Or this:
CObj obj("hello");
Or you could provide a constructor that accepts a const char* :
CObj(const char* cstr) : m_str(cstr) { ... }
I would always advise to make such constructors explicit to avoid unwanted implicit conversions, unless it really brings something to the user of the class.
You are limited to at most one user-defined conversion when instantiating an object (i.e. char[6] -> std::string and std::string -> CObj is one conversion too many).
To fix:
int main()
{
using namespace std::literals::string_literals; // this is necessary
// for the literal conversion
CObj obj = "hello"s; // note the extra "s", (which constructs a std::string)
std::cout << "done" << std::endl;
}

C++ conversion operator overload

'int' and 'double' conversion functions is 'explicit' and in this code why have I permit for use this conversion instead of error message?
If I delete all my conversion overload functions code occur 'conversion error'
class Person
{
public:
Person(string s = "", int age = 0) :Name(s), Age(age) {}
operator string() const { return Name; }
explicit operator int() const{ return 10; } // ! Explicit
explicit operator double()const { return 20; } //! Explicit
operator bool()const { if (Name != "") return true; return false; } // using this
};
int main(){
Person a;
int z = a;
std::cout << z << std::endl; // Why print "1"? Why uses bool conversion?
}
I this it is answer:
Because 'a' can not be convert to int or double it occur error, but because it has bool conversion function, which can convert to int and int to double, code use this function.
int z = a;
Looks innocuous, right?
Well, the above line calls the implicit bool-conversion-operator, because that's the only way you left it to get from Person to int, and that only needs one user-defined conversion (the maximum allowed).
This is the reason for the safe-bool-idiom before C++11, and the major reason for the general advice to mark conversion operators and single-argument ctors explicit unless they are not transformative nor lossy.
If you want to see it for yourself, fix your code and trace invocation of your operator bool.

How to cast a custom type to string in VS2012?

I want to cast a custom class to a string which compiles in VS2005 very well. But in VS2012 I get the compiler error error C2440: 'type cast' : cannot convert from 'A' to 'std::string'. What do I have to change? This is my example:
#include <string>
using namespace std;
class A
{
public:
A& operator=(const char* c);
operator string ();
operator const char* ();
private:
string value;
};
A::operator string () { return string((const char*)(*this)); }
A& A::operator = (const char* aValue) { value = aValue; return *this; }
A::operator const char *() { const char* wort = "Hello"; return wort; }
int main()
{
A a;
string s = (string)a; // C2440
}
The problem is that there are two possible explicit conversions from A to string - via the conversion operator to string; or via the conversion operator to const char *, then via the conversion constructor to string.
Simply making the conversion implicit will resolve the ambiguity; the second conversion requires two user-defined conversions, and so cannot be chosen for an implicit conversion:
string s = a;
However, the class is still a bit flaky, since sometimes you might need an explicit conversion. I would consider removing at least one of the implicit conversion operators - perhaps replace them with explicit operators (if your compiler supports such things), or with named functions (like string itself does with c_str()).
The conversion is ambiguous.
Stay away from C style casts, and prefer to make the conversions explicit.
#include <string>
using namespace std;
class A
{
public:
A& operator=(const char* c);
explicit operator string ();
explicit operator const char* ();
private:
string value;
};
A::operator string () { return string(static_cast<const char*>(*this)); }
A& A::operator = (const char* aValue) { value = aValue; return *this; }
A::operator const char *() { const char* wort = "Hello"; return wort; }
int main()
{
A a;
string s = static_cast<std::string>(a);
}
Your two conversion operators step on each others toes. Each of them works for implicit conversions. But an explicit cast is like a direct constructor call. In your code
string s = (string)a;
is equivalent to
string s = static_cast<string>(a);
which transforms to something like
string tmp(a);
string s(std::move(tmp)); // this move can be elided
// lifetime of tmp ends here
The crux is the direct initialization string tmp(a). There are two viable string constructors: string(const string&) and explicit string(const char *) and your two conversions allow calling either. As neither conversion sequence is better, the call is ambiguous.
By the way, the version using copy initialization without explicit cast is not ambiguous:
string s = a;
should work. Depending on that is very brittle, so not recommended.
You should make your conversion operators explicit (if you are using C++11) or drop one of them.