C++ operator overloading [], where is the data parameter to assign? - c++

I want to add a overload the operator [] in my class. Operator overloading is not something I've had to do before.
I want to write an implementation to do the following:
myclass a;
a["test"] = 123;
int test = a["test"];
So far in my class the prototype looks like this:
string operator[](const char* cpszLabel);
The implementation isn't complete and looks like this:
string myclass::operator[](const char* cpszLabel) {
string strContent;
if ( cpszLabel != nullptr ) {
}
return strContent;
}
What I'm not sure about is how to reference the data that is being assigned or does this require overloading the '=' too?
I've added an overload for the '=' operator, but this doesn't get called:
Prototype:
string operator=(int intData);
Implementation:
string myclass::operator=(int intData) {
char szString[24];
sprintf(szString, "\"%d\"", intData);
return string(szString);
}

You need to arrange things so operator[](const char* cpszLabel) returns a reference to something in your class.
int& operator[](const char* cpszLabel);
is probably a better prototype.
You can then modify that "something" in your class via that reference. To be honest though what you want can be achieved with
typedef std::map<std::string, int> myclass;
and most folk don't bother with the typedef, especially now that we have auto. If you want to use a std::map as a member variable in the class (in order to reduce functionality &c.), then the following is a starting point:
class myclass
{
std::map<std::string, int> m_data;
public:
int& operator[](const char* cpszLabel)
{
return m_data[cpszLabel];
}
};

In a["test"] = 123;, the "receiver" of the assignment is the object that's returned from the lookup, which is a string.
You can't overload string's assignment operator.
But, as is well known, every problem can be solved by introducing a level of indirection.
You can store a type of your own instead of std::string, and let that handle the conversion.
A very small example as illustration:
struct Data
{
template<typename T>
Data& operator=(const T& rhs)
{
std::ostringstream os;
os << rhs;
value = os.str();
return *this;
}
operator const char*() const { return value.c_str(); }
std::string value;
};
struct Container
{
Data& operator[] (const std::string& s) { return table[s]; }
std::map<std::string, Data> table;
};
int main()
{
Container cont;
cont["foo"] = "bar";
cont["baz"] = 123;
cont["goo"] = 5.5;
for (auto v: cont.table)
{
std::cout << v.first << " --> " << v.second << '\n';
}
}
Output:
baz --> 123
foo --> bar
goo --> 5.5

Related

How to call implicitly constructor of value wrapped in shared_ptr which is placed in std::map collection

I'm working with code generator where I can't obtain directly classname of the value which is wrapped in shared_ptr and placed in std::map.
I came to a situation where I need to create new map object but without access to classname I can't perform a valid object constructor call. I tried with the map operator at[], which calls the value constructor, but it calls shared_ptr<T> constructor and the object inside stays uninitialized.
Here the example:
#include <iostream>
#include <map>
#include <memory>
class A
{
public:
A() { std::cout << "A"; }
int getMember() const { return m_member; }
private:
int m_member = 1;
};
int main()
{
std::map<int, A> mapIntToA;
std::map<int, std::shared_ptr<A>> mapIntToAptr;
mapIntToA[1]; // runs A constructor
std::cout << mapIntToA[1].getMember();
mapIntToAptr[1]; // runs shared_ptr constructor
// cant call methods of uninitalized object
// std::cout << mapIntToAptr[1]->getMember();
// this init works, but I can't use classname 'A' in my code generator
// mapIntToAptr[1] = std::make_shared<A>();
return 0;
}
You can use the member types of std::map and std::shared_ptr to get the type of the element.
Something like
using type = typename std::map<int, std::shared_ptr<A>>::mapped_type::element_type;
mapIntToAptr[1] = std::make_shared<type>();
mapIntToAptr.emplace(1, ::std::make_shared<decltype(mapIntToAptr)::mapped_type::element_type>());
Note that use of emplace prevents a situation when map is left with nullptr value when make_shared throws.
operator[] of std::map default constructs absent value.
So, you might wrap std::shared_ptr into a class which constructs you inner class as expected, something like:
template <typename T>
struct shared_ptr_wrapper
{
std::shared_ptr<T> data = std::make_shared<T>();
operator const std::shared_ptr<T>& () const {return data;}
operator std::shared_ptr<T>& () {return data;}
const std::shared_ptr<T>& operator ->() const { return data; }
std::shared_ptr<T>& operator ->() {return data;}
const T& operator *() const { return *data; }
T& operator *() {return *data;}
};
then
std::map<int, shared_ptr_wrapper<A>> mapIntToAptr;
mapIntToAptr[1]; // runs shared_ptr constructor
std::cout << mapIntToAptr[1]->getMember(); // Ok

Access class member "directly" like std::string

I would like the following code to be equivalent:
f = "abc";
h.data = f;
EDIT: I'd also like the ability to do the following:
f += "def"; // f.data == "abcdef";
std::string s = f; // s = "abcdef";
std::cout << f << std::endl;
std::cin >> f;
std::vector<std::string> v (f);
v.push_back(h);
// This would be overkill.
printf("%s", (f + std::string("...\n")).c_str());
Would I need to "inherit" std::string or something? (I'm new to this stuff, so could you show me how?)
Here's my class:
class Foo
{
public:
std::string data;
} f, h;
Add an assignment operator:
class Foo
{
public:
std::string data;
Foo & operator=(const std::string & s) { data = s; return *this; }
};
Depending on what you want to return you could also define it like this:
std::string & operator=(const std::string & s) { data = s; return data; }
Or even, in C++0x:
std::string & operator=(std::string s) { data = std::move(s); return data; }
The former lets you write: Foo x, y; y = x = "hello";. The latter lets you write: std::string a, b; Foo x; a = x = b;. Take your pick.
If I understand correctly, you want to be able to do:
Foo f;
f = "abc";
In which case, you will need to overload operator=. Something along the lines of:
class Foo
{
public:
void operator= (const std::string &str) { data = str; }
std::string data;
};
EDIT: I'd also like the ability to do the following: [...]
[ This would have been better served as a new question but I don't think you could have foreseen that. ]
No, you don't need to inherit from std::string. One possible way to do what you want is to add a conversion operator. (I won't address how to implement operator+=, it can be looked up elsewhere.)
class foo {
std::string data;
public:
foo&
operator=(std::string); // See Kerrek's answer for implementation
operator std::string const&() const
{ return data; }
};
This will do what you want. But I strongly advise you not to use that. Surprising implicit conversions are frowned upon; I recommend reading Herb Sutter to learn why.
Alternatively you can make the conversion operator explicit (as in, declaring it explicit operator std::string const&() const;) to suppress implicit conversions. But that's quite less convenient and readable than adding a member function with an appropriate name:
class foo {
// as before
operator std::string const&() const
{ return as_string(); }
std::string const&
as_string() const
{ return data; }
};
foo f;
// Contrast the uses:
// std::string s0 = f; Not ok; would be an implicit conversion
std::string s0(f); // Ok; explicit conversion
std::string s1 = f.as_string(); // Ok; std::string s1(f.as_string()) works too
std::vector<std::string> v;
// v.push_back(f); Not ok; would use an implicit conversion
v.push_back(static_cast<std::string const&>(f)); // Ok; inconvenient
v.push_back(f.as_string()); // Ok; convenient
Whatever you choose, I still recommend implementing appropriate operators for working with streams:
std::ostream&
operator<<(std::ostream& os, foo const& f)
{
return os << f.as_string();
}
std::istream&
operator>>(std::istream& is, foo& f)
{
std::string extracted;
if(is >> extracted) {
f = std::move(extracted);
}
return is;
}
Overload the = operator.
You can overload the operator.
But I do subscribe to ESA rules about programming. Anything above the simple stuff (eg. I/O, strings, numbers, booleans) get the objects to have member functions. Makes the code more readable and maintainable.
See http://www.esa.int/TEC/Software_engineering_and_standardisation/TECRFBUXBQE_2.html

Is opIndexAssign possible in C++?

The D Programming Language Version 2 has a nifty method to overload an expression like this:
classInstance[someName] = someValue;
Or as D Function defined in this little example:
ref Map opIndexAssign(ref const(ValueT) value, ref const(NameT) name)
{
this.insert(name, value);
return this;
}
Is this possible in C++ (ideally without using the STL)? If so, how?
Normally, you would use a proxy object as the return type of operator[]; that object will have a custom operator= defined. The vector<bool> specialization in the C++ Standard Library uses a proxy to get the behavior you are looking for. The proxy-based solution isn't as transparent as the D version, though. The code is something like:
class proxy;
class my_map {
public:
proxy operator[](const key_type& k);
// Rest of class
};
class proxy {
my_map& m;
key_type k;
friend class my_map;
proxy(my_map& m, const key_type& k): m(m), k(k) {}
public:
operator value_type() const {return m.read(k);}
proxy& operator=(const value_type& v) {m.write(k, v); return *this;}
};
proxy my_map::operator[](const key_type& k) {
return proxy(*this, k);
}
Not being familiar with D, I'm going to have to assume that your first code snippet ends up calling opIndexAssign with value = someValue and name = someName?
If so, it can be done in C++, but not in such a simple manner. You could overload the [] operator, and return a proxy object with a custom = operator as follows (very basic, contrived example):
class MyProxy
{
public:
MyProxy (int& ref) : valueRef(ref) { }
MyProxy& operator = (int value) { valueRef = value; return *this; }
private:
int& valueRef;
};
class MyClass
{
public:
MyProxy operator [] (std::string name);
private:
int myVal;
};
MyProxy& MyClass::operator [] (std::string name)
{
if (name.compare("myVal"))
return MyProxy(myVal);
...
}
int main ( )
{
MyClass mc;
mc["myVal"] = 10; // Sets mc.myVal to 10
}
I'd like to strongly stress that the above is not very pretty/well formed code, just an illustration. It has not been tested.
EDIT: Too quick for me Jeremiah!!!

How can I write a variable of a user-defined type into a stream

I dont mean a variable in a class but a default value for the class as a whole.
struct Scalar {
unsigned int value;
void toThePowerOf(int power);
// etc.
}
I'd like to be able to do something like
Scaler foo;
foo.value = 2;
foo.toThePowerOf(2);
std::cout << foo << std::endl; // Outputs 4!
Is that possible in C++?
No, classes do not have a value in the way you're thinking.
What you probably mean to do is to overload the << operator:
ostream& operator<<(ostream& output, const Scalar& val)
{
output << val.value;
return output;
}
I meant a default value for the class, so if you called that object by just its name foo it would return foo.value by default.
It is actually possible to define an implicit conversion from Scalar to int:
struct Scalar
{
unsigned int value;
operator int() const
{
return value;
}
};
int main()
{
Scalar foo = {2};
std::cout << foo << std::endl;
}
But implicit conversions are generally frowned upon in the C++ community, because it can make code quite hard to read. (I guess this is why noone mentioned conversion operators yet.)
No, you cannot but you can overload operator << for your class and ostream to get the desired effect
std::ostream& operator << (std::ostream& out, const Scaler& foo)
{
return out << foo.value;
}
Your code will now work and produce the desires result
Yes. it is possible. Just initialize all the values in the constructor of the class. Use class instead of struct.
Use a default value in the ctor. Make the ctor explicit if you don't want implicit conversions.
struct Scalar {
unsigned int value;
Scalar(int value=0) : value (value) {}
void toThePowerOf(int power) {
// naive implementation just for show
int new_value = 1;
assert(power >= 0); // or other error checking
for (; power > 0; --power) {
new_value *= value;
}
value = new_value;
}
friend std::ostream& operator<<(std::ostream &out, Scalar const &x) {
out << x.value;
return out;
}
};

Does C++ have an equivilent to Python's __setitem__

Just as the title asks, does C++ have the equivalent of Python's setitem and getitem for classes?
Basically it allows you to do something like the following.
MyClass anObject;
anObject[0] = 1;
anObject[1] = "foo";
basically, you overload the subscript operator (operator[]), and it returns a reference (so it can be read as well as written to)
You can overload the [] operator, but it's not quite the same as a separate getitem/setitem method pair, in that you don't get to specify different handling for getting and setting.
But you can get close by returning a temporary object that overrides the assignment operator.
To expand on Earwicker post:
#include <string>
#include <iostream>
template <typename Type>
class Vector
{
public:
template <typename Element>
class ReferenceWrapper
{
public:
explicit ReferenceWrapper(Element& elem)
: elem_(elem)
{
}
// Similar to Python's __getitem__.
operator const Type&() const
{
return elem_;
}
// Similar to Python's __setitem__.
ReferenceWrapper& operator=(const Type& rhs)
{
elem_ = rhs;
return *this;
}
// Helper when Type is defined in another namespace.
friend std::ostream& operator<<(std::ostream& os, const ReferenceWrapper& rhs)
{
return os << rhs.operator const Type&();
}
private:
Element& elem_;
};
explicit Vector(size_t sz)
: vec_(sz)
{
}
ReferenceWrapper<const Type> operator[](size_t ix) const
{
return ReferenceWrapper<const Type>(vec_[ix]);
}
ReferenceWrapper<Type> operator[](size_t ix)
{
return ReferenceWrapper<Type>(vec_[ix]);
}
private:
std::vector<Type> vec_;
};
int main()
{
Vector<std::string> v(10);
std::cout << v[5] << "\n";
v[5] = "42";
std::cout << v[5] << "\n";
}
It's not portable, but MSVC has __declspec(property), which also allows indexers:
struct Foo
{
void SetFoo(int index, int value) { ... }
int GetFoo(int index) { ... }
__declspec(property(propget=GetFoo, propput=SetFoo)) int Foo[];
}
other than that, Earwicker did outline the portable solution, but he's right that you'll run into many problems.