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;
}
Related
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.
I am trying to write a C++ class that has some overloaded methods:
class Output
{
public:
static void Print(bool value)
{
std::cout << value ? "True" : "False";
}
static void Print(std::string value)
{
std::cout << value;
}
};
Now lets say I call the method as follows:
Output::Print("Hello World");
this is the result
True
So, why, when I have defined that the method can accept boolean and string, does it use the boolean overload when I pass in a non-boolean value?
EDIT: I come from a C#/Java environment, so quite new to C++!
"Hello World" is a string literal of type "array of 12 const char" which can be converted to a "pointer to const char" which can in turn be converted to a bool. That's precisely what is happening. The compiler prefers this to using std::string's conversion constructor.
A conversion sequence involving a conversion constructor is known as a user-defined conversion sequence. The conversion from "Hello World" to a bool is a standard conversion sequence. The standard states that a standard conversion sequence is always better than a user-defined conversion sequence (§13.3.3.2/2):
a standard conversion sequence (13.3.3.1.1) is a better conversion sequence than a user-defined conversion sequence or an ellipsis conversion sequence
This "better conversion sequence" analysis is done for each argument of each viable function (and you only have one argument) and the better function is chosen by overload resolution.
If you want to make sure the std::string version is called, you need to give it an std::string:
Output::Print(std::string("Hello World"));
Not sure why nobody posted this, but you can add another overload that converts from const char* to std::string for you. This saves the caller from having to worry about this.
class Output
{
public:
static void Print(bool value)
{
std::cout << value ? "True" : "False";
}
static void Print(std::string value)
{
std::cout << value;
}
// Just add the override that cast to std::string
static void Print(const char* value)
{
Output::Print(std::string(value));
}
};
FWIW, it can be addressed this way (if templates can be used), if you don't want to add overloads for const char*.
#include <iostream>
#include <string>
#include <type_traits>
template <typename Bool,
typename T = std::enable_if_t<std::is_same<Bool, bool>{}>>
void foo(Bool)
{
std::cerr << "bool\n";
}
void foo(const std::string&)
{
std::cerr << "string\n";
}
int main()
{
foo("bar");
foo(false);
}
Since C++14 we have the operator""s from the std::string_literals namespace, which can be used to tell the compiler to bind to the string (or string_view in C++17) overload:
using namespace std::string_literals;
Output::Print("Hello World"s);
Prints: Hello World
#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[].
I have a class which has a conversion operator for type std::string marked as explicit. Here is the class
class MyClass {
public:
// methods ...
explicit operator std::string() const {
// convert to string ...
}
}
Problem is when I use static_cast on a variable of type MyClass I get the error "No matching conversion for static_cast from 'MyClass' to 'std::string aka …" I seem to have the same problem when I define conversion operators for any custom type. Is the explicit modifier only defined for conversion to primitive types or is this another compiler bug.
Here is an example
#include <iostream>
#include <string>
class MyClass {
public:
// methods ...
explicit operator std::string() const {
return "Hello World";
}
};
int main()
{
MyClass obj;
std::cout << static_cast<std::string>( obj ) << std::endl;
return 0;
}
The output is
Hello World
Problem solved by updating to the latest version of LLVM, which fully supports all C++11 features.
I am trying to write a C++ class that has some overloaded methods:
class Output
{
public:
static void Print(bool value)
{
std::cout << value ? "True" : "False";
}
static void Print(std::string value)
{
std::cout << value;
}
};
Now lets say I call the method as follows:
Output::Print("Hello World");
this is the result
True
So, why, when I have defined that the method can accept boolean and string, does it use the boolean overload when I pass in a non-boolean value?
EDIT: I come from a C#/Java environment, so quite new to C++!
"Hello World" is a string literal of type "array of 12 const char" which can be converted to a "pointer to const char" which can in turn be converted to a bool. That's precisely what is happening. The compiler prefers this to using std::string's conversion constructor.
A conversion sequence involving a conversion constructor is known as a user-defined conversion sequence. The conversion from "Hello World" to a bool is a standard conversion sequence. The standard states that a standard conversion sequence is always better than a user-defined conversion sequence (§13.3.3.2/2):
a standard conversion sequence (13.3.3.1.1) is a better conversion sequence than a user-defined conversion sequence or an ellipsis conversion sequence
This "better conversion sequence" analysis is done for each argument of each viable function (and you only have one argument) and the better function is chosen by overload resolution.
If you want to make sure the std::string version is called, you need to give it an std::string:
Output::Print(std::string("Hello World"));
Not sure why nobody posted this, but you can add another overload that converts from const char* to std::string for you. This saves the caller from having to worry about this.
class Output
{
public:
static void Print(bool value)
{
std::cout << value ? "True" : "False";
}
static void Print(std::string value)
{
std::cout << value;
}
// Just add the override that cast to std::string
static void Print(const char* value)
{
Output::Print(std::string(value));
}
};
FWIW, it can be addressed this way (if templates can be used), if you don't want to add overloads for const char*.
#include <iostream>
#include <string>
#include <type_traits>
template <typename Bool,
typename T = std::enable_if_t<std::is_same<Bool, bool>{}>>
void foo(Bool)
{
std::cerr << "bool\n";
}
void foo(const std::string&)
{
std::cerr << "string\n";
}
int main()
{
foo("bar");
foo(false);
}
Since C++14 we have the operator""s from the std::string_literals namespace, which can be used to tell the compiler to bind to the string (or string_view in C++17) overload:
using namespace std::string_literals;
Output::Print("Hello World"s);
Prints: Hello World