C++ access component via [""] or [int] for custom class - c++

I've noticed things like what follows in C++.
SomeClass obj = SomeClass();
int boo = obj["foo"];
What is this called and how can I do it?
example
class Boo {
public:
int GetValue (string item) {
switch (item) {
case "foo" : return 1;
case "apple" : return 2;
}
return 0;
}
}
Boo boo = Boo();
int foo = boo.GetValue("foo");
// instead of that I want to be able to do
int foo = boo["foo"];

To use [], you'd overload operator[]:
class Boo {
public:
int operator[](string const &item) {
if (item == "foo")
return 1;
if (item == "apple")
return 2;
return 0;
}
};
You might be interested to know std::map already provides pretty much what you seem to be looking for:
std::map<std::string, int> boo;
boo["foo"] = 1;
boo["apple"] = 2;
int foo = boo["foo"];
The obvious difference is that when/if you use this to look up a value that hasn't previously been inserted, it'll insert a new item with that key and the value 0.

This is called operator overloading.
You need to define how the operator [] works:
#include <string>
class Boo {
public:
int operator[] (std::string item) {
if (item == "foo") return 1;
else if (item == "apple") return 2;
return 0;
}
};
Boo boo = Boo();
int foo = boo["foo"];
Also, the switch variable must have integral type so I changed to if else.

You need to overload the [] operator. Here is an example (oddly on a Java site).

What you want is overloading the subscript operator (operator[]); in your case you would do:
class Boo {
public:
int operator[](const string & item) const {
// you can't use switch with non-integral types
if(item=="foo")
return 1;
else if(item=="apple")
return 2;
else
return 0;
}
}
Boo boo = Boo();
int foo = boo["foo"];
Often classes that encapsulate containers return the data by reference, to allow the caller to modify the stored data; that's what most STL containers that provide operator[] do.

Related

Struct with array that changes dynamically

I have been looking to change dynamically the values of an array in a struct depending on other variables of the struct.
Let's say I have:
struct foo
{
int value1 = 0;
int value2 = 0;
int arr[2] = {value1, value2};
};
In the main if I have create an instance fooInstance and I want to associate a value to value1 fooInstance.value1 = 10, how can I update the value in the array ?
Thank you for your time.
Firstly, if you need an array, then I recommend storing the objects in the array directly.
I question the value (i.e. usefulness) of these aliases such as value1 when the name has no more meaning than referring to arr[i] directly. But I can see the value in case there is a descriptive name available. I'll use a more meaningful example of 2D vector with x, y dimensions. It should be easy to change float to int and change the names to match your attempt.
While Frank's solution using functions is great in most regards, it has a small caveat of having a less convenient syntax compared to variables. It's possible to achieve the variable syntax using operator overloading and anonymous unions. The trade-off is the increased boilerplate in the class definition. Example:
union Vector2 {
struct {
float a[2];
auto& operator=(float f) { a[0] = f; return *this; }
operator float&() & { return a[0]; }
operator const float&() const & { return a[0]; }
operator float () && { return a[0]; }
float* operator&() { return &a[0]; }
} x;
struct {
float a[2];
auto& operator=(float f) { a[1] = f; return *this; }
operator float&() & { return a[1]; }
operator const float&() const & { return a[1]; }
operator float () && { return a[1]; }
float* operator&() { return &a[1]; }
} y;
struct {
float a[2];
auto& operator=(float f) { a[0] = a[1] = f; return *this; }
float* begin() { return std::begin(a); }
float* end() { return std::end(a); }
} xy;
};
int main() {
Vector2 v2;
v2.xy = 1337; // assign many elements by name
v2.x = 42; // assign one element by name
std::cout << v2.x; // read one element by name
for(float f : v2.xy) { // iterate the entire array
std::cout << f;
}
}
Note to those unfamiliar with rules of unions: Reading from inactive union member is allowed only through common initial sequence of standard layout structs. This code is well defined, but the reader should be careful to not over generalise and assume that type punning through unions would be allowed; It isn't.
I adapted code from my earlier answer to another question.
It is different parameters coming from different hardwares.
This sounds like generating the accessors shown above with meta programming could be a good approach.
But, if you would like to avoid the complexity, then a more traditional approach would be to just use the array, and use enum to name the indices:
struct foo
{
int arr[100];
enum indices {
name1,
name2,
// ...
name100,
name_count,
};
};
int main()
{
foo f;
f.arr[foo.name1] = 42;
}
If at all possible, use encapsulation. That's the preferred way to create an interface/implementation skew:
struct foo
{
int& value1() { return arr_[0]; }
int& value2() { return arr_[1]; }
int* arr() { return arr_; }
private:
int arr_[2] = {0, 0};
};
void bar(foo& v) {
// access a single value
v.value1() = 3;
// access the whole array
v.arr()[0] = 5;
}
If you need access through both the individual member variables and through an array member variable, do not copy the data; rather, use the array as "the source of truth", and provide access through the individual variables or the individual member functions.
Here is your example rewritten to "alias" array variables to scalar member variables:
struct foo
{
foo() : value1(arr[0]), value2(arr[1]) {}
std::array<int,2> arr;
int& value1;
int& value2;
};
Note: this is not a good way of doing anything in production code, just an illustration of how the language lets you do something like this. Normally I would add accessor member-functions instead of member-variable references, because it avoids many problems referenced in the comments, such as breaking the value semantics.

how to forbid assignment to not reference variables?

I fear it's a dumb question but...
Someone can suggest me a way to force that a return value from a function (or a method), that return a reference to an internal static variable or a member of the class/struct, is assigned only to reference variables ?
I try to explain what I desire with a minimal example.
Given the following code, with a function wrapValue() that return a reference to the internal static variable,
int & wrapValue (int v0)
{
static int val;
return val = v0;
}
int main ()
{
// how to permit this ...
int & v0 { wrapValue(0) };
// ... but forbid this ...
int v1 { wrapValue(1) };
int v2;
// ... and this ?
v2 = wrapValue(2);
}
there is a way to permit the initialization of v0 (and bound v0 to the static variable) and forbid the initialization of v1 and the assignment of v2 (without bounding v1 and v2 to the static variable) ?
And if it's impossible with the current C++ standard, as I fear, someone can suggest me an alternative way (but not too complex: I intend use it in a library that I want to maintain simple) to forbid an unbounded assignment ?
This solution is somewhat tricky but it works (I think) as you expect:
#include <iostream>
struct int_wrapper {
int value;
int_wrapper &operator=(int value) {
this->value = value;
return *this;
}
operator int&() {
return value;
}
operator int() {
return value;
}
};
int_wrapper& wrapValue (int v0) {
static int_wrapper val;
return val = v0;
}
int main () {
// how to permit this ...
int & v0 = wrapValue(0);
// ... but forbid this ...
//int v1 { wrapValue(1) }; // call ambigious
int v2;
(void)v0;
(void)v2;
// ... and this ?
//v2 = wrapValue(2); // call ambigious
}
[live demo]
As far as I know int is copyable, so people can copy if they like; you cannot prevent this. However, you could create a wrapper class that is non-copyable.
class NonCopyableInt
{
int val;
public:
NonCopyableInt(int val) : val(val) {}
NonCopyableInt(NonCopyableInt&) = delete;
int& value() { return val; }
// todo: add some nice operators and functions such as assignment from int
}
NonCopyableInt& wrapValue (int v0)
{
static NonCopyableInt val;
return val = v0;
}
However, people could always copy the return value from value() so you end up with the same problem. And it feels really clunky and meh.

Arithmetic and Assignment operator overloading - return values, scope, combining expressions

The code I have so far:
#include <iostream>
#include <vector>
using namespace std;
class Dictionary
{
private:
string dictName;
struct wordCard
{
string word;
string translation;
};
vector<wordCard> Dict;
bool foundit = false;
public:
// My attemtp at swap function for copy-and-swap:
void swap(Dictionary& dict1, Dictionary& dict2)
{
Dictionary dict3("tmp");
dict3.dictName = dict1.dictName;
dict3.Dict = dict1.Dict;
dict1.dictName = dict2.dictName;
dict1.Dict = dict2.Dict;
dict2.dictName = dict3.dictName;
dict2.Dict = dict3.Dict;
}
// Very basic constructor (setting the dictionary name while creating an object was part of the assignment):
Dictionary(string name)
{
setDictName(name);
}
/* various functions that work fine */
// Overloading "+" operator:
// The result is supposed to be a new dictionary (without changing the source) where all words from the
// original dictionaries are present without doubles.
Dictionary& operator+ (const Dictionary& dict)
{
bool doubleword = false;
string plusname;
plusname = "Augmenting " + this->dictName + " & " + dict.dictName;
Dictionary plusDict(plusname);
plusDict.Dict = this->Dict;
for (int i = 0; i < dict.Dict.size(); i++)
{
doubleword = false;
for (int i2 = 0; i2 < plusDict.Dict.size(); i2++)
{
if (plusDict.Dict[i2].word == dict.Dict[i].word)
{
doubleword = true;
}
}
if (!doubleword)
{
plusDict.Dict.push_back(dict.Dict[i]);
}
}
return *this;
}
/* 2 other overloads that are very similar */
// Overloading "=" operator (using copy-and-swap):
// Not part of the assignment, but I couldn't think of another way to make the other operators work.
Dictionary& operator=(Dictionary dict)
{
swap(*this, dict);
return *this;
}
};
And the problems I have with it:
Ideally, it should work like this:
Obj1 = result of operation Obj2 + Obj3;
What I'm getting at the moment is:
Obj1 = Obj2 (ignores Obj3)
I have a vague idea why it happens (or, actually, two ideas). First, operator+ returns *this, not the actual result. But when I tried to change it to the temp class object, compiler started screaming at me. Second, I'm aware that I'm using a local variable (temp class object), but I don't know how to make it public so I could use it later. When I try to add a class object to the public: section (or private:), the compiler treats it as a function declaration, not a class object.
So, how can I either make my temp class object public, or return result of a+b instead of *this, or make operator= catch the result or operator+ instead of what it returns?
operator + should return a new object by value and be const - i.e. something like
Dictionary operator+ (const Dictionary& dict) const
{
Dictionary ret;
//modify ret
return ret;
}

How to have a set of structs in C++

I have a struct which has a unique key. I want to insert instances of these structs into a set. I know that to do this the < operator has to be overloaded so that set can make a comparison in order to do the insertion.
The following does not work:
#include <iostream>
#include <set>
using namespace std;
struct foo
{
int key;
};
bool operator<(const foo& lhs, const foo& rhs)
{
return lhs.key < rhs.key;
}
set<foo> bar;
int main()
{
foo *test = new foo;
test->key = 0;
bar.insert(test);
}
This might help:
struct foo
{
int key;
};
inline bool operator<(const foo& lhs, const foo& rhs)
{
return lhs.key < rhs.key;
}
If you are using namespaces, it is a good practice to declare the operator<() function in the same namespace.
For the sake of completeness after your edit, and as other have pointed out, you are trying to add a foo* where a foo is expected.
If you really want to deal with pointers, you may wrap the foo* into a smart pointer class (auto_ptr, shared_ptr, ...).
But note that in both case, you loose the benefit of the overloaded operator< which operates on foo, not on foo*.
struct Blah
{
int x;
};
bool operator<(const Blah &a, const Blah &b)
{
return a.x < b.x;
}
...
std::set<Blah> my_set;
However, I don't like overloading operator< unless it makes intuitive sense (does it really make sense to say that one Blah is "less than" another Blah?). If not, I usually provide a custom comparator function instead:
bool compareBlahs(const Blah &a, const Blah &b)
{
return a.x < b.x;
}
...
std::set<Blah,compareBlahs> my_set;
You can overload the operator < inside the class also as,
struct foo
{
int key;
bool operator < (const foo &other) const { return key < other.key; }
};
In your question, if you want to use set<foo> bar; as declaration then, you should insert value as,
bar.insert(*test);
But that won't be a good idea, as you are making redundant copy.
see ereOn's answer, it's right.
The real problem in you code is this:
foo *test = new foo;
test->key = 0;
bar.insert(test);
You insert a pointer in the set, not a struct. Change the insert to:
bar.insert( *test );
// ^
EDIT: but then you'll need to delete foo, as it will be copied in the set. Or just create it on the stack (using set with pointers is not a good idea, because the arrangement will be "strange" - the set will sort according to the pointers' addresses )
The best thing to do is to give foo a constructor:
struct foo
{
foo(int k) : key(k) { }
int key;
};
Then to add, rather than...
foo *test = new foo;
test->key = 0;
bar.insert(test); // BROKEN - need to dereference ala *test
// WARNING: need to delete foo sometime...
...you can simply use:
bar.insert(foo(0));
The problem isn't in your set; it's in your test object. You're using Java style there. In C++, we just write:
set<foo> bar;
int main()
{
foo test; // Local variable, goes out of scope at }
test.key = 0;
bar.insert(test); // Insert _a copy of test_ in bar.
}
In c++11 we can use a lambda expression, I used this way which is similar to what #Oliver was given.
#include <set>
#include <iostream>
#include <algorithm>
struct Blah
{
int x;
};
int main(){
auto cmp_blah = [](Blah lhs, Blah rhs) { return lhs.x < rhs.x;};
std::set<Blah, decltype(cmp_blah)> my_set(cmp_blah);
Blah b1 = {2};
Blah b2 = {2};
Blah b3 = {3};
my_set.insert(b1);
my_set.insert(b2);
my_set.insert(b3);
for(auto const& bi : my_set){
std::cout<< bi.x << std::endl;
}
}
demo

Whats the significance of return by reference?

In C++,
function() = 10;
works if the function returns a variable by reference.
What are the use cases of it?
The commonest case is to implement things like operator[].
struct A {
int data[10];
int & operator[]( int i ) {
return data[i];
}
};
Another is to return a big object from a class via an accesor function:
struct b {
SomeBigThing big;
const SomeBigThing & MyBig() const {
return big;
}
};
in order to avoid the copying overhead.
Consider the following code, MyFunction returns a pointer to an int, and you set a value to the int.
int *i;
i = MyFunction();
*i = 10;
Now shorten that to
*(MyFunction()) = 10;
It does exactly the same thing as the first code block.
You can look at a reference as just a pointer that's always dereferenced. So if my function returned a reference - not a pointer - to an int the frist code block would become
int &i;
i = MyFunction();
i = 10;
and the second would become
MyFunction() = 10;
This is what i was looking for
Getters/setters for instance
class C
{
int some_param_;
public:
int& param() { return some_param_; }
int const& param() const { return some_param_; }
};
but here you should go with some_param being a public int. Containers provide functions that return by reference, eg. vector<T>::operator[] so that you can write v[k] = x.
A very normal use case is when you write an array like class. Here you want to overload the operator [] so as you can do a[0] = 10; In that case you would want the signature to be like int& operator[](int index);
In case you have a class that contains another structure, it can be useful to directly modify the contained structure:
struct S
{
int value;
};
class C
{
public:
S& ref() { return m_s; }
private:
S m_s;
};
Allows you to write something like:
void foo()
{
C c;
// Now you can do that:
c.ref().value = 1;
}
Note: in this example it might be more straightforward to directly make m_s public rather than returning a reference.
SO screwed up my answer
You don't even need to return a reference:
struct C { };
C f() {
return C();
}
int main() {
C a;
f() = a; // compiles fine
}
Because this behavior is quite surprising, you should normally return a const value or a const reference unless the user has a sensible intent to modify the result.
It can be usefull when implementing accessors
class Matrix
{
public:
//I skip constructor, destructor etc
int & operator ()(int row, int col)
{
return m_arr[row + col * size];
}
private:
int size;
int * m_arr;
}
Matrix m(10);
m(1,0) = 10; //assign a value to row 1, col 0
Another classic case:
class Foo {
Foo();
public:
static Foo& getSingleton();
};
std::vector has operator[] which would not allow vec[n] = m otherwise.
You can also achieve method chaining (if you so desire) using return by reference.
class A
{
public:
A& method1()
{
//do something
return *this; //return ref to the current object
}
A& method2(int i);
A& method3(float f); //other bodies omitted for brevity
};
int main()
{
A aObj;
aObj.method1().method2(5).method3(0.75);
//or use it like this, if you prefer
aObj.method1()
.method2(5)
.method3(0.75);
}
The named parameter idiom is a another use case. Consider
class Foo
{
public:
Foo(
int lions,
float tigers,
double bears,
std::string zookeeper
);
};
users of this class need to remember the position of each parameter
Foo foo( 1, 2.0, 5, "Fred" );
which can be non-obvious without looking at the header. Compared to a creator class like so
class CreateFoo
{
friend class Foo;
public:
CreateFoo();
CreateFoo& lions(int lions) {
_lions = lions;
return *this;
}
CreateFoo& tigers(float tigers) {
_tigers = tigers;
return *this;
}
CreateFoo& bears(double bears) {
_bears = bears;
return *this;
}
CreateFoo& zookeeper(const std::string& zookeeper) {
_zookeeper = zookeeper;
return *this;
}
private:
int _lions;
float _tigers;
double _bears;
std::string _zookeeper;
};
which can then be used by clients like so
Foo foo = CreateFoo().
lions(1).
tigers(2.0).
zookeeper("Fred").
bears(5)
;
assuming Foo has a constructor taking a const CreateFoo&.