Hi I came across this piece of code. It demonstrates how to work with matrix structures of the dlib library.
According to this one can initialize a matrix structure by:
M = 54.2, 7.4, 12.1,
1, 2, 3,
5.9, 0.05, 1;
How can this be possible in C++?
Is this some kind of operator overloading?
Logic
This is possible by overloading operator, (operator comma), and for example make it push new floating point values into M.
A thing to notice is that operator, should always have at least one parameter of class type, therefore you'll have to create a class that is implicitly convertible to floating point value (for example via a non explicit constructor with 1 argument of type double or float).
Example
For example we'll try to do this for a wrapper type over an std::vector and we'll try to make M = 1, 2, 3, 4, 5 a valid expression which results in an std::vector with those elements in sequence. You'll see that this is easily applicable to the matrix example you gave.
A thing to remember is that operator= has more precedence over operator, (as shown in this table of operator precedence); therefore M = 1, 2, 3, 4, 5 will really be parsed as: (((((M = 1), 2), 3), 4), 5).
Given that, we'll start by creating our class container with an operator= that takes a single value and pushes it into the container:
template<typename ValueType>
struct container {
explicit container(std::size_t n) {
vec.reserve(n);
}
container& operator=(ValueType a) {
vec.push_back(a);
return (*this);
}
std::vector<ValueType> vec;
};
At this point we can define operator, as:
template<typename ValueType>
container<ValueType>& operator,(container<ValueType>& m, ValueType a) {
m.vec.push_back(a);
return m;
}
which just pushes back a new element value.
And now you can easily see that the following works just fine and prints 1 2 3 4 5:
int main() {
container<int> M(5);
M = 1, 2, 3, 4, 5;
for (auto i : M.vec) std::cout << i << ' ';
}
Live demo
Considerations
I discourage this technique as much as possible. It forces you to have weird semantic for other operators, such as operator= and really seem to add nothing to a simple use of std::initializer_list<T>.
A saner example would be to have operator= as follows:
container& operator=(std::initializer_list<ValueType> a) {
std::copy(begin(a), end(a), back_inserter(vec));
return (*this);
}
and then simply use brackets:
int main() {
container<int> M(5);
M = { 1, 2, 3, 4, 5 };
for (auto i : M.vec) std::cout << i << ' ';
}
Live demo
Related
I have a class A for which I've overloaded the operator[] such that if object a of class A can have an std::valarray passed to it in the following manner:
std::valarray<int> in{2,1};
auto b = a[in]; // lets say this returns an int k = 2
auto c = a[{2,1}]; // also returns an int k = 2
However, when using the GTEST framework, it doesn't like the a[{2,1}] notation, and complains that macro "EXPECT_EQ" passed 3 arguments, but takes just 2 if we are to try a test such as EXPECT_EQ(2, a[{2,1}]);
Surely a[{2,1}] is a single argument, since it evaluates to an int? This doesn't seem to be the case. A typical EXPECT_EQ such as EXPECT_EQ(2, 2); is just fine.
Below, I have a MWE of a class that overloads [] to takes a parameter of type std::valarray.
class A
{
private:
std::vector<int> elems;
public:
A(std::vector<int> elems)
: elems(elems){};
int operator[](std::valarray<int> idx) const
{
return get_elem(idx[0]);
}; // getter
int get_elem(int idx) const {return this->elems[idx];}; //getter
int &get_elem(int idx) {return this->elems[idx];}; //setter
};
int main()
{
std::vector<int> elems = {2, 5, 0, 9,
5, 1, 4, 6};
A a(elems);
std::cout<< "hello world" << std::endl;
std::valarray<int> in{2,1};
auto b = a[in]; // lets say this returns an int k = 2
auto c = a[{2,1}]; // also returns an int k = 2
std::cout<< b << std::endl;
std::cout<< c << std::endl;
return 0;
}
Given that my main() displays correct behaviour, I suspect that there is an issue with GTEST, or is evaluation order in C++ different from what I expect?
Since macros are involved, and there is no getting rid of them, the quickest solution is a judicious use of parentheses:
EXPECT_EQ(2, ( a[{2,1}] ));
The extra pair will prevent the comma from being interpreted as an argument separator for the macro. As for the comparison itself, a parenthesized expression has the exact same value and value-category as the expression withing the parentheses. So everything should still work1.
1 - Unless Google Test applies decltype to that second sequence of tokens, then the extra parentheses can potentially cause surprises. But it shouldn't be doing that.
Here's a part of Eigen documentation:
Matrix3f m;
m << 1, 2, 3,
4, 5, 6,
7, 8, 9;
std::cout << m;
Output:
1 2 3
4 5 6
7 8 9
I couldn't understand how could all the comma separated values be captured by operator<< above. I did a tiny experiment:
cout << "Just commas: ";
cout << 1, 2, 3, 4, 5;
cout << endl;
cout << "Commas in parentheses: ";
cout << ( 1, 2, 3, 4, 5 );
cout << endl;
Predictably (according to my understanding of C++ syntax) only one of the values was captured by operator<< :
Just commas: 1
Commas in parentheses: 5
Thus the title question.
The basic idea is to overload both the << and the , operators.
m << 1 is overloaded to put 1 into m and then returns a special proxy object – call it p – holding a reference to m.
Then p, 2 is overloaded to put 2 into m and return p, so that p, 2, 3 will first put 2 into m and then 3.
A similar technique is used with Boost.Assign, though they use += rather than <<.
This is a possible simplified implementation
struct M3f {
double m[3][3];
struct Loader {
M3f& m;
int i;
Loader(M3f& m, int i) : m(m), i(i) {}
Loader operator , (double x) {
m.m[i/3][i%3] = x;
return Loader(m, i+1);
}
};
Loader operator<<(double x) {
m[0][0] = x;
return Loader(*this, 1);
}
};
The idea is that << returns a Loader instance that waits for second element, and each loader instance uses the comma operator to update the matrix and returns another loader instance.
Note that overloading the comma operator is generally considered a bad idea because the most specific characteristic of the operator is strict left-to-right evaluation order. However when overloaded this is not guaranteed and for example in
m << f(), g(), ...
g() could end up being called before f().
Note that I'm talking about the evaluation order, not about associativity or precedence that are of course maintained also for overloaded versions.
For example g() could be called before f() but the result from f() is guaranteed to be correctly placed in the matrix before the result from g().
The comma itself is an operator in c++ which can be overloaded (and apparently is by eigen). I don't know the exact way eigen implements the overloading but I am sure that you can search for the sources of eigen to look it up.
To your little experient you have to understand how the unoverloaded comma operator works in c++.
The comma operator has the form <statement>,<statement> and is evaluated to whatever the second statement is evaluated to. The operator << has a higher precedence than the operator ,. Because of that the cout is evaluated before the rest of the comma operations are evaluated.
Because , is Left-to-right associative the code (1,2,3,4,5) is equal to ((((1,2),3),4),5) which evaluates to the most right value, which is 5.
Let's say I am developing a class Vec<T>, which represents a mathematical vector with elements of type T.
For convenience I created the constructor that takes std::initializer_list:
Vec(std::initializer_list<T> l)
: size(l.size())
{
data = new T[size];
std::copy(l.begin(), l.end(), data);
}
So now I can do the following things:
v += Vec<int>({ 1, -1 });
bool equal = (v == Vec<int>({ 8, 9 }));
And so on... However, it would be great if I could write it even shorter and cleaner:
v += { 1, -1 };
bool equal = v == { 8, 9 };
How do I achieve such behavior? I suppose I could overload a type-conversion operator to std::initializer_list, but is it even considered normal practice? How bad is it for compilation time and performance, and does it even work?
What about std::vector, does it support something like that in C++11?
EDIT:
So, here's my operator+=:
Vec<T> & operator+=(const Vec<T> &v)
{
assert(size == v.size);
for (int i = 0; i < size; ++i)
data[i] += v.data[i];
return *this;
}
The answers in the comments are correct, initializer_list actually works after assignment operators:
v += {2, 3};
v -= {2, 3};
I was trying to write something like this:
v = v + {2, 3};
That's why it didn't work.
So, the conclusion is: you can use it like this after assignment and compound-assignment operators, but for binary arithmetic operators and comparisons it won't work, am I correct? I suppose creating custom literal is not an option either.
By the way, how about:
const Vec<float> a{ 1.01, 2.02 }; // error: conversion from 'double' to 'float' requires a narrowing conversion test_app
const Vec<float> b{ 1.01f, 2.02f }; // works, obviously
Can I do something to enable implicit conversion in the first case?
EDIT2
Here is the operator+:
friend Vec<T> operator+(Vec<T> v, const Vec<T> &w)
{
v += w; // reuse compound assignment
return v; // return the result by value (uses move constructor)
}
Unfortunately, v + {1, 2} is not a well-formed expression in C++ for grammatical rules. Most binary operators only take expressions for each of the operands, and the braced list is not an expression. The compound assignment operator += is special, because assignment also accepts an initializer-clause for the right-hand operand, so v += {1, 2} happens to work.
Some alternatives:
operator+(v, {1, 2})
v + Vec<int>({1, 2})
How about v.equals({8, 9})? Also, while v + {1, 2, 3, 4} is not possible, you might still overload the comma operator as many vector-matrix implementations do (not for int, but for a specific type, like: v + (V(1), 2, 3, 4), add macros if you like them).
Here's a part of Eigen documentation:
Matrix3f m;
m << 1, 2, 3,
4, 5, 6,
7, 8, 9;
std::cout << m;
Output:
1 2 3
4 5 6
7 8 9
I couldn't understand how could all the comma separated values be captured by operator<< above. I did a tiny experiment:
cout << "Just commas: ";
cout << 1, 2, 3, 4, 5;
cout << endl;
cout << "Commas in parentheses: ";
cout << ( 1, 2, 3, 4, 5 );
cout << endl;
Predictably (according to my understanding of C++ syntax) only one of the values was captured by operator<< :
Just commas: 1
Commas in parentheses: 5
Thus the title question.
The basic idea is to overload both the << and the , operators.
m << 1 is overloaded to put 1 into m and then returns a special proxy object – call it p – holding a reference to m.
Then p, 2 is overloaded to put 2 into m and return p, so that p, 2, 3 will first put 2 into m and then 3.
A similar technique is used with Boost.Assign, though they use += rather than <<.
This is a possible simplified implementation
struct M3f {
double m[3][3];
struct Loader {
M3f& m;
int i;
Loader(M3f& m, int i) : m(m), i(i) {}
Loader operator , (double x) {
m.m[i/3][i%3] = x;
return Loader(m, i+1);
}
};
Loader operator<<(double x) {
m[0][0] = x;
return Loader(*this, 1);
}
};
The idea is that << returns a Loader instance that waits for second element, and each loader instance uses the comma operator to update the matrix and returns another loader instance.
Note that overloading the comma operator is generally considered a bad idea because the most specific characteristic of the operator is strict left-to-right evaluation order. However when overloaded this is not guaranteed and for example in
m << f(), g(), ...
g() could end up being called before f().
Note that I'm talking about the evaluation order, not about associativity or precedence that are of course maintained also for overloaded versions.
For example g() could be called before f() but the result from f() is guaranteed to be correctly placed in the matrix before the result from g().
The comma itself is an operator in c++ which can be overloaded (and apparently is by eigen). I don't know the exact way eigen implements the overloading but I am sure that you can search for the sources of eigen to look it up.
To your little experient you have to understand how the unoverloaded comma operator works in c++.
The comma operator has the form <statement>,<statement> and is evaluated to whatever the second statement is evaluated to. The operator << has a higher precedence than the operator ,. Because of that the cout is evaluated before the rest of the comma operations are evaluated.
Because , is Left-to-right associative the code (1,2,3,4,5) is equal to ((((1,2),3),4),5) which evaluates to the most right value, which is 5.
Is there any common way to get rid of custom 'assign' functor? std::transform could be great but according to c++ standard it prohibits modification of the source elements
The goal is to modify collection elements using as more declarative approach as possible
template <typename T>
struct assign : std::binary_function<T, T, void> {
void operator()( const T& source, T& dest ) {
dest = source;
}
};
int main() {
static boost::array<int, 5> arr = { 1, 2, 3, 4, 5 };
std::for_each( arr.begin(), arr.end(),
boost::bind( assign<int>(), boost::bind( std::plus<int>(), _1, 3 ), _1 ) );
return 0;
}
std::transform() does allow the output iterator to point to the same element as the beginning of your input range. See here. The code example shows essentially this in the line demonstrating a transform with two input ranges. The output iterator is the same as the first input iterator. Does that make it more palatable?
Try Boost.Lambda:
int main() {
using namespace boost::lambda;
static boost::array<int, 5> arr = { 1, 2, 3, 4, 5 };
std::for_each( arr.begin(), arr.end(), _1 += 3 );
return 0;
}
However, what about just a "for_each" loop:
int main() {
static boost::array<int, 5> arr = { 1, 2, 3, 4, 5 };
BOOST_FOREACH(int& n, arr) {
n += 3;
}
return 0;
}
What #gregg said plus:
The wording in the standard (N1905 25.2.3 paragraph 2) forbids the transformation function from directly modifying elements in the given ranges, i.e. it cannot write to an input iterator. It is only supposed to calculate a value, which the transform function then assigns to the result iterator. In fact, paragraph 5 explicitly allows result to be equal to first.
I guess this may allow implementations to perform safety-checks or optimizations in specific cases.
For instance, suppose transform were specialized for std::vector<char>. On a 32 bit machine, the implementation could unroll the main loop 4 times and perform 32 bit loads and stores instead of 8 bit ones. Clearly this would not work, if the first call to the transformation function modified the input range (the remaining 3 calls before the store would then operate on dirty data).
Here's C++03 one-liner to add 3 to each value off arr:
std::transform(arr.begin(), arr.end(), arr.begin(), std::bind1st(std::plus<int>(), 3));