How does auto deal with multiple values in () initializer? - c++

I know for C++11 way of initializing a vector using auto, actually an std::initializer_list is initialized instead of a vector.
However, given below piece of code:
#include <iostream>
#include <vector>
using namespace std;
int main() {
auto x = {1, 2};
cout << typeid(x).name() << endl;
auto z = (1, 2);
cout << z << ", type: " << typeid(z).name() << endl;
return 0;
}
I don't understand:
Why the type of x returned is St16initializer_listIiE and the type of 'z' returned is 'i', using gcc-10 compiler. Shouldn't we just return std::initializer_list and 'int'?
There is a warning on z: warning: left operand of comma operator has no effect [-Wunused-value]. Then the 2nd half of result is: 2, type: i. How does c++11 interpret ()-initialized type? Why is only the last element passed into z and thus z is still of type int?

The only thing that makes an initializer list is {}. In
auto z = (1, 2);
what you have is the comma operator which only returns that last value. So that means your code boils down to
auto z = 2;
and since 2 is an int, z is an int.

Why the type of x returned is St16initializer_listIiE and the type of 'z' returned is 'i', using gcc-10 compiler. Shouldn't we just return std::initializer_list and 'int'?
typeid() won't directly give what you would expect, it just returns the specific type identification as written down in its code. If you would like to decrypt the same, pass it via c++filt:
c++filt -t St16initializer_listIiE
It will result in what you were expecting, i.e. an:
std::initializer_list<int>
There is a warning on z: warning: left operand of comma operator has no effect [-Wunused-value]. Then the 2nd half of result is: 2, type: i. How does c++11 interpret ()-initialized type? Why is only the last element passed into z and thus z is still of type int?
( ) is an initializer holding an expression, or a list of expressions. If you were to assign that to auto, it would select the very last item in the expression list as its type as it would be seperated by commas till the advent of the last expression, which ideally tells auto to assign its type to the last one.
For Example:
auto x = (1, 1.5L);
would result in a long.
auto x = (1, 1.5f);
would result in a float.
auto x = (1, "string");
would result in a const char pointer.
It entirely neglects the first value within the ( ) initializer, which is an int.

Because { 1, 2 } is an std::initializer_list<int>, but (1, 2) is an expression, that expands to comma-operator (it evaluates both arguments and returns the second one as a result, so (1, 2) is collapsed to (2), which is collapsed into 2. That's the reason, why auto z = (1, 2); evaluates into an integer initialization.
Because the result of instruction 1 is simply ignored (remember (1, 2) calculates both expressions and throws away the result of the first one).

Related

About Logical operator using variable parameter in templates

I don't know what's happening in this code.
SimpleFunction(1,2,3) is equal to
1&&(2&&3) //1
1||(2||3) //1
SimpleFunction(1) is equal to
1&&Something //1
1||Something //1
SimpleFunction() is equal to
voidvalue (&&) //1
voidvalue (||) //0
What is 'Something?'
and what is happening in SimpleFunction(void)??
Is it special something about Logical operator in unary fold??
#include<iostream>
using namespace std;
template <typename ...Ts>
void SimpleFunction(Ts ... ts)
{
cout<<(ts && ...)<<endl;
cout<<(ts || ...)<<endl;
}
int main()
{
SimpleFunction(); // 1, 0
cout<<endl;
SimpleFunction(1); // 1, 1
cout<<endl;
SimpleFunction(1,2,3); // 1, 1
cout<<endl;
return 0;
}
When the pack contains zero elements, unary && and || folds are defined to return true and false, respectively. In fact, they are two of the only three operators that allow folding with zero elements, with the last one being the comma operator ,, which returns a prvalue of type void. This is why code like nums + ... should usually be rewritten as nums + ... + 0.
When the pack contains one element, a fold expression always expands to the only element.
Why? This is what the standard says. Because it is defined the only way that makes sense. There isn't much more to say other than that.

C++ unexpected value type of range-v3 partial_sum view

Consider the following minimal example:
#include <range/v3/all.hpp>
#include <iostream>
namespace rng = ranges::v3;
int main()
{
std::vector<int> v { 6, 2, 3, 4, 5, 6 };
auto f = [](auto a, auto b) { return a*0.3 + b*0.7;};
auto rng = v | rng::view::partial_sum(f);
for(auto i : rng)
{
std::cout<<i<<" ";
}
}
This outputs
6 3 2 3 4 5
I would have expected to see double numbers here, but the result are obvisouly integers. This is in contrast to the behavior of view::transform.
The reason for this is because in the implementation, the running-sum value has a type that corresponds to the source range:
semiregular_t<range_value_type_t<Rng>> sum_;
Is this intended or a bug?
Discussion: I see the trouble one is running into when trying to get a valid return type, as as the transformation function is using both the source range and the result range as parameters and produces a return type. The next application uses the source-range-type and this return type to produce another (possibly different) return type, and so on.
By this, in principle, one is repeatedly chaining the source-value-type with the result types of the transformation function. This repeated iteration yields something usable only if the result type "converges" to a specific type to which all other intermediate results can be converted to (in the example above, this type is double, which is obtained already after the first call of the transformation function).
With this observation one could propose a workaround: apply the binary transformation function a given number of times and the use the common_type as value type of the resulting range (if one finds a convergence, prematurely stop). In the simplest case, the number of iterations is just one. If this iteration does not lead to something reasonable, one can still resort to the source-value-type (or a compiler error).
To make it clear, here is the application for the example above:
First iteration : f(int,int) -> yields "double"
Second iteration: f(int,double) -> yields "double"
Third iteration : f(int,double) -> yields "double"
After the third iteration the pattern converges, so stop and choose the common-type double as the value_type of the returned range.
I'm not sure whether this approach is completely valid in all theoretical circumstances, but at least it gives a double in the first example -- which I guess is what everyone is strongly expecting.
ranges::view::partial_sum by design mirrors the semantics of std::partial_sum. If you run:
#include <iostream>
#include <iterator>
#include <numeric>
#include <vector>
int main()
{
std::vector<int> v { 6, 2, 3, 4, 5, 6 };
auto f = [](auto a, auto b) { return a*0.3 + b*0.7; };
std::vector<double> rng;
std::partial_sum(v.begin(), v.end(), std::back_inserter(rng), f);
for(auto i : rng)
{
std::cout<<i<<" ";
}
}
You should get exactly the same output as from the program in the OP. Like many range-v3 views, this view's job is to compute the same sequence of results as computed by a standard algorithm, but do so lazily.
std::partial_sum is specified to operate on an accumulator whose type is the same is the value type of the input range. [partial.sum]/2 says:
Effects: For a non-empty range, the function creates an accumulator acc whose type is InputIterator's value type, initializes it with *first, and assigns the result to *result. For every iterator i in [first + 1, last) in order, acc is then modified by acc = acc + *i or acc = binary_­op(acc, *i) and the result is assigned to *(result + (i - first)).
To behave equivalently, ranges::view::partial_sum also uses an accumulator whose type is the value type of the input range.
In the case of the OP, you can achieve the desired result by using double as the type of the input range. With range-v3 this is easy to do on-the-fly by composing with ranges::view::transform(ranges::convert_to<double>{}):
#include <range/v3/all.hpp>
#include <iostream>
namespace rng = ranges::v3;
int main()
{
std::vector<int> v { 6, 2, 3, 4, 5, 6 };
auto f = [](auto a, auto b) { return a*0.3 + b*0.7;};
auto rng = v | rng::view::transform(rng::convert_to<double>{}) |
rng::view::partial_sum(f);
for(auto i : rng)
{
std::cout<<i<<" ";
}
}
which produces the desired output:
6 3.2 3.06 3.718 4.6154 5.58462

C++ Vector Values not correct

Im in the middle of creating an application with openSG and C++.
Can anyone tell me, why these lines(174ff):
Vec3f snakeDirection = (1,2,3);
std::cout << "direction"<< snakeDirection<<"\n";
prints
direction 3,0,0
the complete code for my scene can be found here:
http://pastie.org/9420616#20,177
In C++ the expression (1,2,3) forces the compiler to interpret the commas inside the parentheses as the sequential-evaluation operator.
The sequential-evaluation operator is a binary operator that evaluates its first operand as void and discards the result, it then evaluates the second operand and returns its value and type. Therefore, the expression (1,2,3) is going to be evaluated in the following way:
First 1 is evaluated and discarded, then (2,3) is evaluated and the result (2,3) is returned.
First 2 is evaluated and discarded, then 3 is evaluated and the result 3 is returned.
Consequently, the evaluation of the expression (1,2,3) will return 3.
Thus, stating:
Vec3f snakeDirection = (1,2,3);
is the same as stating:
Vec3f snakeDirection = 3;
What you've probably intended to write is either:
Vec3f snakeDirection = Vec3f(1, 2, 3);
or
Vec3f snakeDirection(1, 2, 3);
or
Vec3f snakeDirection{1, 2, 3};
or
Vec3f snakeDirection = {1, 2, 3};

Assigning a struct in C++

I have a struct defined with a constructor as such:
struct Point {
double x; double y;
Point (double xx=0, double yy=0): x(xx),y(yy){};
};
I can create points like this and this work fine,
Point P0(0,0), P1(1,1);
But I want to be able to modify it as such:
P0 = (4,5); //no compilation error but garbage returned when printing P0
Does not work!
Also, I have a function like I am using like this
void Foo(Point P0, Point P1){
...do stuff
}
and I want to pass the following argument like this:
Foo((1,2),(2,3)); //no compilation error but garbage returned
I tried looking all this up not really clear to me.. any help
P0 = (4,5);
This does not do what you think it does.
The expression (4,5) evaluates to 5. This is because it contains what's called the comma operator. The comma operator first evaluates its left operand, throws away the result, and then evaluates its second operand. So this statement is really equivalent to
P0 = 5;
Now, this is in turn equivalent to
P0 = Point(5);
which is equivalent to
P0 = Point(5, 0);
since you have a default argument. It's not garbage at all. It's perfectly well-defined.
Precisely the same thing is going to happen in your other statement:
Foo((1,2),(2,3)); // same as Foo(Point(2, 0), Point(3, 0));
To fix this, you can construct the points explicitly:
P0 = Point(4, 5);
Foo(Point(1, 2), Point(2, 3));
or, if you have C++11 support, you can do this:
P0 = {4, 5};
Foo({1, 2}, {2, 3});
(When a comma occurs inside braces, it's not a comma operator. It separates elements of the initializer list.)
(4,5) is equivalent to just 5, since that's how the comma operator works: it evaluates the first expression, discards the result, then evaluates the second. You should get a compilation warning about a discarded expression with no side-effects, if you enable warnings.
In C++11, you can use a list initialiser to do what you want:
P0 = {4,5};
Foo({1,2},{2,3});
If you're stuck with an older dialect, you'll have to spell it out:
P0 = Point(4,5);
Foo(Point(1,2), Point(2,3));
In this statement
P0 = (4,5); //no compilation error but garbage returned when printing P0
expression (4,5) is an expression with the comme operator. Its value is the second subexpression that is 5.
Then the compiler tries to convert value 5 to an object of type Point that to assign it to P0. As you have conversion constructor
Point (double xx=0, double yy=0): x(xx),y(yy){};
then the compiler calls it with arguments 5 and 0 that is as Point( 5, 0 ). This temporary object is assigned to P0.
You should use braced init list instead of the comma operator. For example
P0 = { 4, 5 };
provided that your compiler supports this feature of the C++ 11.
Otherwise you have to specify explicitly the constructor in the right side of the expression
P0 = Point( 4, 5 );
You should use P0 = Point(4, 5) instead. Point(4, 5) call creates a temporary object which is then copied to P0 (in fact it is usually optimized so no actual copying is required).

std::vector operator==: value or reference comparison?

So, I was trying to come up with a solution for When does x==x+2 in C++ on codegolf, and came up with this snippet only to realize that I don't know how it works. I'm not sure why both of these conditions evaluate to true.
Does anyone know if the line labeled line: is true because x==&x or because x+2 is evaluated before the left-hand side of ==?
#include <iostream>
#include <vector>
std::vector<int>& operator+ ( std::vector<int> &v, int val )
{
v.push_back(val);
return v;
}
int main()
{
std::vector<int> x;
std::vector<int> y = x + 2; // y is a copy of x, and x is [2]
// how are both of these are true?
std::cout << (x==y) << "\n"; // value comparison [2]==[2]
line:
std::cout << (x==x+2) << "\n"; // reference comparison? &x == &(x+2)
// not sure if this is relevant
std::cout << (x+2==x) << "\n"; // also true
return 0;
}
It seems--since vectors appear to be compared by value--that if x were evaluated before x+2, then x wouldn't be equal to x+2 (by value). I'm probably missing something obvious. Thanks in advance.
For the standard containers, operator== is overloaded as std::equal. This in turn works on itera­tors and applies comparison by dereferncing, as in *it1 == *it2. Therefore, no copies are required.
The expression x == x + 2 is the same as operator==(x, x + 2). Both operands are evaluated before the function call, and since x + 2 modifies x, both operands are the same. Thus the equality holds.
The surprise is the result of your unconventional design choice in overloading the +-operator. This is ge­ne­rally poor practice and a taboo in any collaborative project. If you absolutely must overload opera­tors, then only if they behave as expected, follow established semantics and are not surprising. The usu­al behaviour of the +-operator is to return a new object, by value, and leave the operand unaffected. Like so:
std::vector<int> operator+(std::vector<int> v, int n)
{
v.push_back(n);
return v;
}
std::vector's equality comparison performs a lexicographical compare that checks that the size of lhs and rhs are the same, and then compares element by element.
The problem with your code is that you are assigning x+2 to y, and your addition operator is modifying the lhs, acting like a += operator.
Here:
std::vector<int> y = x + 2;
this modifies x, and copy assigns y from x. A well behaved operator+ would be something like
std::vector<int> operator+ ( std::vector<int> v, int val )
{
v.push_back(val);
return v;
}
The confusion arises from the unconventional definition of +. Normally, it would return a modified copy of its argument, leaving the argument itself unchanged. Since the operator acts more like +=, modifying its argument and returning a reference to it, this is roughly equivalent to:
x.push_back(2), x == x
comparing the modified vector to itself.
C++ always compares values, never references; if you want a reference comparison, then you must explicitly compare addresses, &x == &y.
std::vector::operator==() is (usually?) a function, and this is a sequence point in C++03, which means that it needs to fully evaluate all of it's parameters before it can be called.
Both parameters resolve to the same reference (vector x), so it is only natural that it would evaluate to true.
This is because that operator+ never creates a new object, it just modifies one.
It is, in fact, equivalent to this code:
std::vector<int> x;
x.push_back(2);
std::cout << (x==x) << "\n"; // this is no surprise