I'd really like to be able to assign a std::string object from a DecoratedString object that I'm writing.
class DecoratedString
{
private:
std::string m_String;
public:
DecoratedString(const std::string& initvalue)
: m_String(initvalue)
{
}
const std::string& ToString() const
{
return m_String;
}
const std::string& operator=(const DecoratedString& rhs)
{
return rhs.ToString();
}
}
I've written a unit test to make sure this works:
void DecoratedStringTest::testAssignmentToString()
{
std::string expected("test");
DecoratedString sut(expected);
std::string actual;
actual = sut;
CPPUNIT_ASSERT_EQUAL(actual, sut.ToString());
}
However, the compiler says error: no match for 'operator=' in 'actual = sut'. It then lists the overloaded operator= options from the standard library.
Why isn't the compiler finding the operator= I defined?
EDIT:
So I guess I need a conversion operator, not an assignment operator. Huge thanks to the people that saw what I was trying to do and explained what I should do instead.
The operator = you have defined is for assigning decorated strings to other decorated strings and returning an std::string from that assignment.
What you want is a member "conversion operator" that automatically converts a decorated string to an std::string whenever required, like this:
operator std::string const &() const { return ToString(); }
That will also convert a decorated string automatically to a std::string const & whenever one is needed (i.e. when comparing to an std::string, or passing a DecoratedString to a function which takes a std::string const &).
The compiler is complaining about this line:
actual = sut;
which should be:
actual = sut.ToString();
Alternatively you should provide a cast operator to implicitly convert a DecoratedString to a std::string:
class DecoratedString
{
...
operator std::string() const
{
return ToString();
}
};
You're overloading the unary assignment operator that is intended for assigning a specific type to DecoratedString and is supposed to return a reference to DecoratedString so you can chain assignments. This operator is not designed to allow you to assign an object of DecoratedString to a different datatype, but rather so you can assign an object that isn't necessarily a DecoratedString to a DecoratedString. It also gives you a well-defined function to handle any sort of specific processing that an assignment of your class might required (deep copy, for example).
You either have to call your ToString() function or you'll have to create a conversion operator that can convert your DecoratedString into a std::string by implementing the following member function for DecoratedString:
operator std::string () const;
This might or might not be a good idea as this conversion operator will be used implicitly by the compiler and might lead to unexpected behaviour.
As an aside, another reason why your operator overload is not working is that you're trying to overload a function by its return value, which is a big no-no in C++.
You got it backwards. The operator= needs to be defined on the std::string to be able to accept your object, not the class you are assigning from, but since you are dealing with the standard library you can't do that.
An alternative would be a streaming operator (as a free function):
std::string& operator<<( std::string& out, const your_class& yc )
{
out.assign( yc.to_string());
return out;
}
your_class myobj;
std::string str;
str << myobj;
Or just define regular streaming for std::ostream and use std::stringstream.
Edit:
Yet another option, as others noted, is the type conversion operator:
your_class::operator const std::string() const;
Modern practice though tells us it's not the greatest idea (temporaries, extra copies, surprises).
Related
#include "booking.h"
#include <iostream>
booking::booking ( const std::string p_title, const std::string p_notice, const category p_category, const person p_person, const booking::Type p_type, const double p_value ) :
m_type{ p_type },
m_title{ p_title },
m_notice{ p_notice },
m_person{ p_person },
m_category{ p_category },
m_value { p_value }
{
std::cout << "Booking was created" << std::endl; // Debug Message
}
These are the files (everything thats necessary to know in my opinion)
#pragma once
#include <string>
#include "person.h"
#include "category.h"
class booking
{
public:
enum Type { TYPE_REVENUE, TYPE_EXPENDITURE };
booking ( const std::string p_title, const std::string p_notice, const category p_category, const person p_person, const booking::Type p_type, const double p_value ); //Basic Constructor
~booking();
Type GetType ( );
std::string GetTitle ( );
std::string GetNotice ( );
category GetCategory ( );
double GetValue ( );
private:
Type m_type;
std::string m_title;
std::string m_notice;
category m_category;
person m_person;
double m_value;
};
If i put one of the class members (like m_type or the double value, it doesnt matter which) to const, it throws following error:
Fehler 1 error C2280: booking &booking::operator =(const booking &) : attempting to reference a deleted function C:\Program Files (x86)\Microsoft Visual C++ Compiler Nov 2013 CTP\include\utility 53
I dont get why the compiler complains about the copy constructor and whats basicly the matter.
You can't (reasonably) assign to an object of a class that has const members.
That's why you get an error about the copy assignment operator.
You're not getting a complaint about the copy constructor.
In other news:
In C++ ALL UPPERCASE names are a convention for macros. If they're used for anything else (e.g. constants, as in Java) then you increase the risk of name collisions and inadvertent text replacement. Besides it's an eyesore, read by many as extra heavy emphasis. Java doesn't have a preprocessor. C++ does have one.
It's a good idea to pass non-basic-type arguments in general as reference to const (you only added const). There are some extra considerations for large arguments that are copied. In C++11 these are best passed by value and moved.
Simple "getter" member function should be declared const so that they can be called on a const object.
Regarding the Java-inspired Get prefixes, consider GetSin(u)+GetCos(v) versus sin(u)+cos(v). In Java a Get prefix can have some value for tools that use introspection. Java has introspection. C++ doesn't have instrospection. The conventions employed should better be adapted to the language used.
When you declare a const member, the compiler does not generate a default assignment operator (it has no clue what to do with this member during assignment, after all, it is const ?), you will have to write the assignment operator yourself.
Note:
pass you parameters by reference to const.
operator= is not the copy-constructor, it is the assignment operator.
const objects cannot be updated, so in your assignment operator you cannot modify the object.
If you don't declare your own assignment operator, the compiler generates one for you which does member-wise copy. But if there is a const member, this doesn't work, so it can't generate the assignment operator after all. (In C++11 this is called having a deleted assignment operator).
Finally, if you have some code that tries to use the assignment operator, then you get this error about attempting to use the deleted assignment operator. Some of the standard library containers or algorithms require the assignment operator to be present. You didn't show all your code but somewhere you will have tried to perform an operation that requires assignment.
referring this thread, as to the copy constructor with const members,
the general form of the copy constructor is,
class Foo {
Foo( const Foo& f ) :
mem1( f.mem1 ), mem2( f.mem2 ) /* etc */
{}
};
where mem1 and mem2 are data members of Foo, which can be const
members, non-const members, const references or non-const references.
Recently I learned about function's reference qualifiers, e.g.
struct foo
{
void bar() {}
void bar1() & {}
void bar2() && {}
};
Where I might need this feature, is there any real use case for this language feature ?
Where I might need this feature, is there any real use case for this language feature ?
The example you show is pretty useless, it's more useful when you have an overloaded function, one version that operates on lvalues and one that operates on rvalues.
Consider a type a bit like std::stringstream that owns a string and returns it by value. If the object is an rvalue, it can move the string instead of copying it.
class StringBuilder
{
public:
std::string get() const& { return m_str; }
std::string get() && { return std::move(m_str); }
private:
std::string m_str;
};
This means when you return a StringBuilder from a function and want to get the string out of it, you don't need a copy:
std::string s = buildString().get();
More generally, given a function f(const X&) if it would be useful to overload it with f(X&&), then given a member function X::f() it might be useful to change it to X::f() const& and overload it with X::f()&&
There are basically two uses:
To provide an optimized overload, for example to move a member out of a temporary object instead of having to copy it.
Prevent misuse of an API. For example, no one would expect
int a = 1 += 2;
to work and this would also cause a compile error. However
string b = string("foo") += "bar";
is legal if operator += is declared as
string & operator += (string const & o);
as is usually the case. Also this has the nasty side-effect of providing an lvalue-reference to your rvalue. Bad idea. This can easily be prevented by declaring the operator as
string & operator += (string const & o) &;
#include "booking.h"
#include <iostream>
booking::booking ( const std::string p_title, const std::string p_notice, const category p_category, const person p_person, const booking::Type p_type, const double p_value ) :
m_type{ p_type },
m_title{ p_title },
m_notice{ p_notice },
m_person{ p_person },
m_category{ p_category },
m_value { p_value }
{
std::cout << "Booking was created" << std::endl; // Debug Message
}
These are the files (everything thats necessary to know in my opinion)
#pragma once
#include <string>
#include "person.h"
#include "category.h"
class booking
{
public:
enum Type { TYPE_REVENUE, TYPE_EXPENDITURE };
booking ( const std::string p_title, const std::string p_notice, const category p_category, const person p_person, const booking::Type p_type, const double p_value ); //Basic Constructor
~booking();
Type GetType ( );
std::string GetTitle ( );
std::string GetNotice ( );
category GetCategory ( );
double GetValue ( );
private:
Type m_type;
std::string m_title;
std::string m_notice;
category m_category;
person m_person;
double m_value;
};
If i put one of the class members (like m_type or the double value, it doesnt matter which) to const, it throws following error:
Fehler 1 error C2280: booking &booking::operator =(const booking &) : attempting to reference a deleted function C:\Program Files (x86)\Microsoft Visual C++ Compiler Nov 2013 CTP\include\utility 53
I dont get why the compiler complains about the copy constructor and whats basicly the matter.
You can't (reasonably) assign to an object of a class that has const members.
That's why you get an error about the copy assignment operator.
You're not getting a complaint about the copy constructor.
In other news:
In C++ ALL UPPERCASE names are a convention for macros. If they're used for anything else (e.g. constants, as in Java) then you increase the risk of name collisions and inadvertent text replacement. Besides it's an eyesore, read by many as extra heavy emphasis. Java doesn't have a preprocessor. C++ does have one.
It's a good idea to pass non-basic-type arguments in general as reference to const (you only added const). There are some extra considerations for large arguments that are copied. In C++11 these are best passed by value and moved.
Simple "getter" member function should be declared const so that they can be called on a const object.
Regarding the Java-inspired Get prefixes, consider GetSin(u)+GetCos(v) versus sin(u)+cos(v). In Java a Get prefix can have some value for tools that use introspection. Java has introspection. C++ doesn't have instrospection. The conventions employed should better be adapted to the language used.
When you declare a const member, the compiler does not generate a default assignment operator (it has no clue what to do with this member during assignment, after all, it is const ?), you will have to write the assignment operator yourself.
Note:
pass you parameters by reference to const.
operator= is not the copy-constructor, it is the assignment operator.
const objects cannot be updated, so in your assignment operator you cannot modify the object.
If you don't declare your own assignment operator, the compiler generates one for you which does member-wise copy. But if there is a const member, this doesn't work, so it can't generate the assignment operator after all. (In C++11 this is called having a deleted assignment operator).
Finally, if you have some code that tries to use the assignment operator, then you get this error about attempting to use the deleted assignment operator. Some of the standard library containers or algorithms require the assignment operator to be present. You didn't show all your code but somewhere you will have tried to perform an operation that requires assignment.
referring this thread, as to the copy constructor with const members,
the general form of the copy constructor is,
class Foo {
Foo( const Foo& f ) :
mem1( f.mem1 ), mem2( f.mem2 ) /* etc */
{}
};
where mem1 and mem2 are data members of Foo, which can be const
members, non-const members, const references or non-const references.
I have a class like that.I want to use reference for string but it doesnt work.How can i use string& ?
#include <string>
using namespace std;
class T
{
public:
T(string& s);
private:
string s;
};
T::T(string& s)
{
this->s = s;
}
int main(void)
{
T t("Test Object");
return 0;
}
Error : 'T::T(std::string &)' : cannot convert parameter 1 from 'const char [12]' to 'std::string &'
Use const :
class T
{
public:
T(const string& ss);
private:
string s;
};
T::T(const string& ss) : s(ss)
{
}
"Test Object" will be constructed as a const string before passing to T's constructor, so the constructor has to accept a const string.
You're not passing in a std::string. As it says, it can't convert from const char array to string-ref. Change the constructor to take a const-ref and it'll work. This is because a temporary string will have to be created from the char-array, and you can only make const-references to temporaries to stop you getting confused (if you modify the temporary, the changes are just going to be discarded, so the language stops you from doing that).
In C++98/03, if you have a class that is not cheap to copy (e.g. an int or a double are cheap to copy, a std::string isn't, since its copy can involve allocating new heap memory, copying characters from source to destination, etc.), then the rule is to pass by const reference const std::string&:
class T
{
public:
T(const string& s); // <--- const string&
private:
string m_s;
};
And then in constructor do:
T::T(const string& s)
: m_s(s)
{}
However, in C++11, where move semantics is available, the new rule seems to be: if you need a copy (and the object is cheap to move, as it normally should be), pass by value and move from the value:
T::T(string s) // pass by value
: m_s( std::move(s) ) // and move from the value
{}
(The optimal thing would be to offer a couple of overloads, passing by const & and passing by value, but probably this is not necessarily in all applications, but only when you need to squeeze performance.)
Note that when you don't need a copy, and just need to observe the parameter, the usual C++98/03 pass by const & rule is still valid.
The constructor you defined takes a std::string by reference:
T::T(std::string& s)
{
this->s = s;
}
thus the most straightforward thing to do would be to create a std::string object, that will be passed to this constructor:
std::string s("Test Object");
T t(s);
But since your constructor doesn't change the std::string you pass to it (it is just used to set the value of T's data member) you should pass const reference: T::T(const string& s). Also instead of letting the data member s being constructed and assigning another string into it later, it would be better if you construct this member directly within an initialization list:
T::T(const std::string& str) : s(str) { }
References need to be intialized using the initialiser-list of the constructor. Change your constructor to:
T::T(string& s) : s(s)
{
}
Additionally define your member s as std::string& s to be able to take a reference.
Maybe change the name of s to avoid ambiguities.
See this entry on SO.
I just came across various overloading methods like type of parameter passed, varying number of parameters, return type etc. I just want to know that can I overload a function with following two version
//function which can modify member
String& MyClass::doSomething();
//constant member function
String& MyClass::doSomething() const;
Please let me know the reason behind it.
Yes you can.
If you have
MyClass m;
m.doSomething();
The non-const version will be called. When you have
const MyClass m;
m.doSomething();
The const version will be called.
The easy way to understand this, is to think of the object you are calling the method on as the 0th parameter:
e.g.: Think of
String& MyClass::doSomething();
MyClass mc;
mc.doSomething();
as a fancy, compiler supported way of doing this:
String& MyClass_doSomething(MyClass *this);
MyClass mc;
MyClass_doSomething(&mc);
Now, overloading based on constness is just a special case of overloading by parameter types:
String& MyClass::doSomething();
String& MyClass::doSomething() const;
can by thought of as something like this:
String& MyClass_doSomething(MyClass *this);
String& MyClass_doSomething(const MyClass *this);
As to the usefullness of this, the easiest examples are the begin and end methods on various std:: containers. If the vector/string/whatever is non const, and you call begin(), you'll get back an iterator that can be used to modify the contents of the original container. Clearly, this shouldn't be allowed for containers marked const, so, thanks to the const/non const overloading, calling begin() on a const vector return a const_iterator that can be used to read the contents of the vector, but not modify it.
e.g.
string nc = "Hello world";
for (string::iterator iString = nc.begin(); iString != nc.end(); ++iString)
{
cout << *iString;; // legal to read
*iString = toupper(*iString); // legal to write
}
const string c = "Hello world again";
for (string::const_iterator iString = c.begin(); iString != c.end(); ++iString)
{
cout << *iString;; // legal to read
// *iString = toupper(*iString); // Writing is illegal
}
As to overloading by return type, you can't do it in C++. However, you can simulate it fairly decently.
Yes, you can do this.
(#Job, this is not overload on return type, this is overload on 'const-state' of the called class instance)
If the instance is const, the second variant will be called, if it isn't, the first one is called.
The practical use is that if the method is called on a const instance, you probably also want to return a "const String&" (e.g. if you return a reference to a member of the class), like this:
//function which can modify member
String& MyClass::doSomething();
//constant member function
const String& MyClass::doSomething() const;
This is how iterator is used. You have a const iterator as well as a non-const one.
Not only is this possible, it as a commonly used idiom.
For example, if the const version of a function is called, a reference to a read-only version of a result can be returned. But if the non-const version is called, a copy is constructed and returned that the receiver may modify. By implementing a const version, we are saved from performing the construction when we know it isn't going to be needed.