i just wrote a function:
void doSomeStuffWithTheString(const std::string& value) {
...
std::string v = value;
std::cout << value.c_str();
...
}
but then i call this with
doSomeStuffWithTheString("foo");
and it works. So i would have thought that this to work (a const char* to initialise a implicit instance of std::string) the value would have to be passed by value, but in this case is passed by (const) reference.
Is by any chance a implicit temporal std::string instantiated from const char* when the reference is const? if not, then how this possibly work?
EDIT
what happens if the function is overloaded with
void doSomeStuffWithTheString(const char* value);
which one will choose the compiler?
The std::string type has an implicit conversion (via constructor) from const char*. This is what allows the string literal "foo" to convert to std::string. This results in a temporary value. In C++ it's legal to have a const & to a temporary value and hence this all holds together.
It's possible to replicate this trick using your own custom types in C++.
class Example {
public:
Example(const char* pValue) {}
};
void Method(const Example& e) {
...
}
Method("foo");
Yes, a temporary std::string is constructed from the string literal.
Exactly, using std::string default constructor
"the value would have to be passed by value, but in this case is passed by (const) reference."
There is a C++ feature where it is possible to pass a temporary value (in this case, a temporary std::string implicitly converted from the const char *) to an argument of const-reference (in this case, const std::string &) type.
Related
I am writing a code in C++ right now, and I have a dilemma.
EDIT: I added the class relevant definition
_fullName is defined in the class as a private dm(char _fullName)
my class contains the getter method below:
char* getFullName() const { return this->_fullName; }
However, I had in the past cases in which I returned char* with const(const char*)
I can change the getter method to this one:
const char* getFullName() const { return this->_fullName; }
Both versions are compiling but I don't know how to choose.
I would take the second version, but I wonder Why even the version without the const is compiling? shouldn't it give an error when I remove the const keyword, because the function is a CONST member function and therefore the dms are const as well and cannot be returned without the const keyword???
This is the class relevant definition:
class Professional
{
private:
char* _ID;
char* _fullName;
int _age;
char* _profession;
}
First understand what const attached to a member function means. It determines the const-ness of the implicit this used for the member. Given this:
struct S
{
char value[10];
const char *mfn() const { return value; }
};
within the confines of mfn the implicit this is const S *. Furthermore, all references to members obtained therein are also const. In this case, that means value as an expression representation is const char (&)[10] before any further conversion are attached therein.
That's important.
In the correct posted example above there is an automatic conversion to temporary pointer, since const char(&)[10] converts to const char* without objection. However, the following will fail:
struct S
{
char value[10];
char *mfn() const { return value; }
};
The reason this will fail is simply because there is no conversion possible. value, remember, is const char (&)[10], and can only implicitly convert to const char*.
But how could it succeed? Well, consider this:
struct S
{
char *ptr;
char *mfn() const { return ptr; }
};
This will work. The reason is subtle, but obvious once you think about it. ptr is a char*. The attachment of const to this makes that char * const&, not const char *& (there is a difference; the former is a reference to a non-mutable pointer to mutable data, the latter is a reference to a mutable pointer to non-mutable data). Since return ptr is not mutating ptr (or anything else in the object), it is viable as a result.
In short, in your case it works because the pointer you are returning isn't via some implicit conversion from a const underlayment; it's just a value.
You have a few things going on here, in your example of the int num member this is not the same as a char * member.
lets expand your example a bit:
class Professional
{
int* func() const
{
return &myInt; // Fails because it returns a reference to a member variable
}
char* func() const
{
return &myChar; // Fails because it returns a reference to a member variable
}
int* func() const
{
return pInt; // ok because it returns a COPY of the member pointer
}
char* func() const
{
return pChar; // ok because it returns a COPY of the member pointer
}
int myInt;
char myChar;
int *pInt;
char *pChar;
Note: the two pointers getting returned by copy are ok. Its the pointers that are copies not the thing being pointed to. The memory that is pointed to is nothing to do with this class.
As to an answer to your question: there is a rule of thumb that is to apply const as much as possible - this helps to show other users your intent and gains benefits from compiler warnings / errors if a user tries to use your class in a way you did not intend. So in this case pass back a const char *.
Note: See this answer for more specific details here: What is the difference between const int*, const int * const, and int const *?
I am reviewing C++ references any am trying to reason why the following piece of code complies:
#include <string>
class Foo {
public:
Foo(const std::string& label)
: d_label(label) {}
private:
std::string d_label;
};
int main(int argc, const char** argv) {
Foo("test");
return 0;
}
Here, we are assigning a reference to a const string to a string. In doing so, is a copy of label made that is non-const? If so, why is it that we can make a copy of a const object that is itself non-const? Otherwise, what exactly is going on here, in terms of copy constructor/assignment calls?
In C++ the keyword const actually means read-only. To make a copy of an object, you don't need write access. Therefore, you can copy a const std::string into an std::string.
Also note, that copying in C++ means making a deep copy by default. This is called value semantics. Hence, manipulating the copied string will not do anything to the original string.
Now to your last question: What is going on in the following line?
Foo("test");
The type of "test" is const char[5]. The compiler searches for a matching constructor of Foo. Since "test" is implicitly convertible to std::string via the
basic_string<CharT,Alloc>::basic_string( const CharT * s, const Alloc & a = Alloc() );
constructor, this conversion will be performed, i. e. a temporary std::string is constructed from "test". A const reference to this temporary is then passed to the constructor
Foo::Foo( const std::string & label );
This constructor in turn calls std::strings copy constructor in order to construct the d_label member of Foo:
basic_string<CharT,Alloc>::basic_string( const basic_string & other );
C++ offers both reference semantics and value semantics: Objects have values, and then you can take references to objects.
std::string d_label
d_label is an object holding a string value. It is considered to own the bytes holding the string as a memory resource. This notion of ownership rationalizes using d_label as an interface to modify the string.
const std::string& label
label is a read-only reference to a string. This is not quite the same as "a reference to a const string." It refers to an object that may be (and probably is) not const.
: d_label(label)
This initializes d_label with the content of label using a copy constructor. You may then do what you like with the copy. You know that the process of copying won't modify the underlying object of label because label was declared const &.
Here no assignments take place. d_label(label) initializes the variable d_label with label which subsequently ends up calling copy constructor of string type.
Let's have a closer look:
Foo("test"); initializes const std::string& label with "test" which its type is const char[5]
Here a string reference (label) is getting initialized with a value having type const char[5]. This initialization is valid since "test" through decaying to const char * can be passed to one of string constructors which gets a const char *.
Now label is a reference to a real string object storing "test".
Then, d_label(label) initializes Foo::d_label with the object referred to by label.
At this point copy constructor of string type is called and constructs the Foo::d_label.
I am trying to push a string in a string vector, like below
void Node::set_val(string &val)
{
this->val.push_back(val);
}
But when I try to call it as below
Obj.set_val("10h;");
I get the below error,
error: no matching function for call to 'Node::set_val(const char [5])'
I assumed that the string in " " is same as string in c++, Why do I get such an error? What has to be changed below?
You are taking in a std::string by non-const reference. Non-const references cannot bind to rvalues, like "10h;", so you can't pass literals in to that function.
If you aren't going to modify the argument, you should take your argument by reference-to-const:
void Node::set_val(const string &val)
// ^^^^^
This way, a temporary std::string will be constructed from your const char[5] and passed in to set_val.
You could improve this by taking in the string by value and moveing it into the vector:
void Node::set_val(string val)
{
this->val.push_back(std::move(val));
}
This prevents you from making some unnecessary copies.
So in C++, const char* is implicitly convertible to std::string because std::string has a (non-explicit) constructor that takes const char*. So what the compiler tries here is to create a temporary std::string object for your function call, like so:
Node.set_val(std::string("10h;"));
However, since you declared the parameter of set_val to be a non-const reference to a std::string, the compiler can't make this conversion work due to the fact that temporary objects can't be bound to non-const references.
There are three ways to make this work, depending on what you want to achieve:
void Node::set_val(const std::string& val) {}
void Node::set_val(std::string val) {}
void Node::set_val(std::string&& val) {}
All will compile (the last one requires C++11 or higher), but seeing your use case, I would recommend to use the second or third one. For an explanation why, try reading a little bit about move semantics in C++11.
The important thing to take away here is that const char* implicitly converts to std::string by creating a temporary object, and temporary objects can't be passed to functions taking non-const references.
You are passing "10h;" which is a const char array.
Fix it by passing a string: Obj.set_val(string("10h")); and edit function to take a string by value:
void Node::set_val(string val) { /* */ }
Or maybe better, edit your function to take a const string&:
void Node::set_val(const string &val) { /* */ }
Is the following code acceptable in C++? If so, what happens? Does it create a temp string variable and pass its address?
void f(const string& s) {
}
const char kJunk[] = "junk";
f(kJunk);
Yes, it's acceptable. The compiler will call the string(const char *) constructor and create a temporary that will be bound to s for the duration of the call. When the fall to f returns the temporary will be destroyed.
The argument that is the character array is implicitly converted to a temporary object of type std::string and the compiler passes const reference to this temporary object to the function. When the statement with the call of the function will finish its work the temporary object will be deleted.
Does it create a temp string variable and pass its address?
Yes, it's equivalent to:
void f(const std::string& s) {
}
const char kJunk[] = "junk";
f(std::string(kJunk));
I have a fairly simple class that looks like this:
class Person {
public:
Person(string name): _name(name) {};
void greet(const Person& person) const {
cout << "Hello, " << person._name << "!" << endl;
};
private:
string _name;
};
Note that the greet method takes a parameter of the Person type. When I pass it a Person object, it works as expected. Now let's pass it a string as a parameter in this way:
Person maher("maher");
maher.greet("sam");
When trying to run that code in QT (on a machine running ubuntu), it generates the following error:
no matching function for call to ‘Person::greet(const char [4])’
I was able to resolve this error by casting the string in this way: maher.greet(string("sam"));
My question is the following: Why can't c++ 'see' that I'm passing a string to the greet method? Does it have anything to do with the fact that the greet method accepts a Person object?
maher is a const char[6], and sam is a const char[4], and both decay to const char * implicitly, but none of them is actually a std::string.
In function calls, the C++ standard allows an implicit conversion to be performed if there's a non-explicit constructor of the target type that accepts the type of the actual value passed to the function.
This is what happens when you call the constructor: you pass a const char[6], which automatically decays to a const char *; the target type is std::string, which has a constructor that accepts a const char *; such constructor is called, and the Person constructor correctly receives his std::string parameter.
In the second case, this is not happening: Person does not have a constructor that accepts a const char *, but only a constructor that accepts a std::string. To reach the desired Person type the compiler would have to first convert the const char * to std::string, and then call the Person constructor. This double conversion is not allowed, mainly because overloading resolution would become a complete mess (which already is) with lots of ambiguous cases.
If you want to allow greet to be called with a C-style string you should either:
create a constructor for Person which accept a C-style string (const char *), so that it can be constructed directly from a const char *, without going through the prohibited extra conversion
create another overload for greet to accept an std::string.
On the other hand, IMO the cleaner alternative is just leave it as it is; the caller will just have to write
maher.greet(std::string("sam"));
You aren't passing a std::string, you are passing a C-style string with type const char*. Add a constructor for that too:
Person(string name): _name(name) {};
Person(const char *name): _name(name) {};
Note that while const char* automatically can convert to std::string, in this case that would mean 2 conversions (const char * > std::string > Person), which is not allowed.
maher.greet("sam");
This requires two conversions:
First, const char[4] to std::string so that Person(string) can be called.
Then std::string to Person so that greet(const Person&) can be called.
But chain conversion is not allowed.
So you either provide a constructor Person(const char*), or pass std::string to avoid first conversion (listed above):
If you provide a constructor Person(const char*), then const char[4] will directly convert to Person which then will be passed to greet().
But if you pass std::string to greet(), then std::string will convert to Person using the existing constructor in your code, and then the person object will be passed to greet() eventually.
In both cases, there is only one conversion.
Or, simply write:
//convert const char[4] to std::string manually.
maher.greet(std::string("sam"));
It absolutely does "see" that you're passing a string and that's the problem. You don't have a method that accepts a string as input.
Does it have anything to do with fact that the greet method accepts a Person object?
Yes,
You can pass a string to greet() method because there is a constructor in your Person class which constructs a Person object through the string that is passed to it.
Passing a char[4] requires the char[4] to a string object first ant then apply the above mentioned conversion. This chaining of conversions is not allowed and hence.
You could override the method to accept a string.
void greet(const string name) const {
cout << "Hello, " << name << "!" << endl;
};