Data member and rvalue life-time - c++

Somehow inspired by the expression template code in Expression templates and C++11, written by Paul Preney, I decided to test the following:
template<typename T>
struct X
{
X(T t) : t(std::forward<T>(t)) {}
T t;
};
template<typename T>
auto CreateX(T&& t) -> X<decltype(std::forward<T>(t))>
{
return X<decltype(std::forward<T>(t))>(std::forward<T>(t));
}
Then, I used it to produce an instance of X<const vector<int>&> and X<vector<int>&&> as follows:
int main()
{
int vec = {1,2,3,4};
auto x1 = CreateX(vec);
auto x2 = CreateX(vector<int>{5,6,7,8});
cout << "x1: "; for(auto x : x1.t) cout << x << " "; cout << endl;
cout << "x2: "; for(auto x : x2.t) cout << x << " "; cout << endl;
}
The output is:
x1: 1 2 3 4
x2: 0 0 33 0 0 0 7 8
which shows that the life-time of the temporary vector<int>{5,6,7,8} is not being extended, and the rvalue-reference member X::t binds to something else.
Okay, from this answer What is the lifetime of the class data member which const reference to a rvalue?, I know this is the expected behaviour.
However, the question here is: what is different in Paul Preney' code in Expression templates and C++11 that permits the temporary vectors to exist as long as the rvalue-references members exist? See his Case 2, where temporaries are created.
Apparently, the same construct there is used here, but I am probably missing something.
Edit:
Based on the answer of R. Martinho Fernandes below, I tried the following:
int main()
{
using namespace std;
auto expr = math_vector<3>{1.0, 1.1, 1.2} + math_vector<3>{2.0, 2.1, 2.2};
cout << "vec1: "; for(int i = 0; i < 3; ++i) cout << expr.le()[i] << " "; cout << endl;
cout << "vec2: "; for(int i = 0; i < 3; ++i) cout << expr.re()[i] << " "; cout << endl;
}
and it turns out that this is a valid code that outputs:
vec1: 1.0 1.1 1.2
vec2: 2.0 2.1 2.2
Therefore, apparently references stored in the expression template is not dangling. What is going on here?

what is different in Paul Preney' code in Expression templates and C++11 that permits the temporary vectors to exist as long as the rvalue-references members exist?
Nothing permits such a thing.
The temporary vectors there exist until the end of the full expression, as any other temporary that isn't bound to a local reference variable. That is enough in Paul's code because the code immediately materialises the expression tree into an actual math_vector, and the temporaries are not needed anymore after that.
Paul's code does not store any expression template node (math_vector_expr) anywhere, while your code stores one (X) as x2. That's a known issue with auto: it does the wrong thing when you are using expression templates because it results in storing the expression tree, which is likely to contain references that will become dangling right away.
In order to make it totally clear, the following is fine.
math_vector<3> result =
math_vector<3>{1.0, 1.1, 1.2} +
math_vector<3>{2.0, 2.1, 2.2} +
math_vector<3>{3.0, 3.1, 3.2} +
math_vector<3>{4.0, 4.1, 4.2}
; // no references are held to any temporaries past this point
The following is not fine.
math_vector_expr<3> result = // or auto
math_vector<3>{1.0, 1.1, 1.2} +
math_vector<3>{2.0, 2.1, 2.2} +
math_vector<3>{3.0, 3.1, 3.2} +
math_vector<3>{4.0, 4.1, 4.2}
; // result now holds references to those temporaries

The problem - as R. Martinho Fernandes states in his answer - is that you capture references to rvalues in your "expression tree" which outlive their referents. The solution is to only store references to lvalues, and to capture objects passed by rvalue reference directly. In other words, store their values in the expression tree instead of by reference (Live code at Coliru):
template<typename T>
struct X
{
X(T t) : t(std::move(t)) {}
T t;
};
// Capture lvalue references
template<typename T>
X<const T&> CreateX(const T& t)
{
return X<const T&>(t);
}
// Copy rvalue references
template<typename T>
typename std::enable_if<std::is_rvalue_reference<T&&>::value,X<T>>::type
CreateX(T&& t)
{
return X<T>(std::move(t));
}
If the user passes you an lvalue reference, it is her responsibility to ensure that the referent outlives the expression tree object.

Related

Understanding std::pair <const T&, const U&>

I am trying to create a class that instantiates an std::pair<const T&, const U&> from two member variables. I started this out of curiosity and was surprised to see the results from creating multiple objects of, in the example below, Pair.
struct Pair {
int num;
string s;
pair<const int&, const string&> pr{make_pair(num, s)};
Pair(int n, const string& s) : num(n), s(s) {}
};
ostream& operator<<(ostream& out, const Pair& p) {
out << setw(2) << p.num << ", " << p.s << ": <" << p.pr.first << "," << p.pr.second << ">";
return out;
}
/******************************************************************************/
int main() {
cout << string(15, '-') << " PIECEMEAL " << string(15, '-') << endl;
Pair p1{1, "normal1"};
Pair p2{2, "normal2"};
cout << p1 << endl;
cout << p2 << endl << endl;
cout << string(15, '-') << " VECTOR " << string(15, '-') << endl;
vector<Pair> v;
for (int i=0; i< 05; ++i) {
v.emplace_back(i, "from_vec'" + ::to_string(i) + "'");
}
for(const auto & p : v) {
cout << p << endl;
}
}
I thought that the const reference type declarations within the pair would refer to the Pair members, num and s, respectively. However, when I create multiple Pairs, it appears that the references within the pair end up referencing (to some degree) the Pair members of the most recently constructed Pair.
Here is the output:
--------------- PIECEMEAL ---------------
1, normal1: <2,normal2>
2, normal2: <1586964272,normal2>
--------------- VECTOR ---------------
0, from_vec'0': <4,from_vec'4'>
1, from_vec'1': <1586964272,from_vec'4'>
2, from_vec'2': <1586964272,from_vec'4'>
3, from_vec'3': <1586964272,from_vec'4'>
4, from_vec'4': <1586964272,from_vec'4'>
I looked at cppreference in an attempt to understand what was going on, but still not grasping it.
Why do the references within the pair no longer reference the Pair members, num and s?
There are 2 problems with your code.
First:
pair<const int&, const string&> pr{make_pair(num, s)}
std::make_pair(num, s) first create a temporary pair<int, string>. Then pr will be initialized with this temporary pair.
Note that pair<int, string> is not the same as pari<const int&, const string&>, which means it will not trigger the default move constructor.
Instead, if you looked at cppreference, this would trigger the 6th overload, and essentially assign pr.first and pr.second with this temporary pair, hence dangling reference.
To fix it, you should specifically create a pair of references with:
pr{std::make_pair(std::ref(num), std::ref(s)}
Or just create pr directly with num and s:
pr{num, s}
Second:
v.emplace_back(i, "from_vec'" + ::to_string(i) + "'");
This line would trigger the default move constructor, since you didn't specify one yourself, which would perform a member-wise move on Pair.
However, what happens when you move pr to the new Pair? The old pr was referencing the old num and old s. Moving the old pr to the new Pair doesn't change the value of pr, hence it will continue referencing the old num and old s, hence dangling reference.
So instead of relying on default generated move constructors, you must define them manually:
Pair(Pair&& pair) noexcept
: num(std::exchange(pair.num, {})
, s(std::move(pair.s))
{}
Note, you don't need to construct pr within initializer list since you already have a default initializer for pr.
Demo
You are inadvertently creating a pair that holds const references to another pair that was allocated on the stack and immediately freed. In other words, undefined behavior.
Step by step, here's what's happening when you instantiate a Pair:
The Pair constructor is called.
The Pair initializer is called.
The initializer calls make_pair, creating a pair of int and std::string on the stack.
This temporary pair gets assigned to pr, and the const references inside it now refer to the memory of the temporary pair.
The initializer exits, freeing the temporary pair on the stack. the int and string references inside pr now refer to memory that has been freed.
What's more, you really shouldn't be storing references to other members inside your struct, for memory safety reasons. If you want a std::pair of your two values, you should make a member function that returns it instead of storing it inside the struct itself.

Making a list of generators (vector of lambdas) leads to very strange behavior with capture-by-reference

The following code is fairly similar to my actual application. Basically, I am trying to create a vector of functions so that I can generate a very large output in segments. I don't fully understand how the capture by reference [&] is working / should be working, and it's leading to some weird behavior.
#include <iostream>
#include <functional>
#include <vector>
using namespace std;
template <typename T>
T add(const T& a, const T& b) {
return a + b;
}
template <typename T>
T add(const T& a, const T& b, T x) {
return (add<T>(a,b)*x);
}
int main() {
std::cout << "Hello World!\n";
vector<function<long ()>> funks;
for (long i = 1; i < 12; ++i) {
//auto funky = std::bind(add<int>, i, i*i);
std::cout << "PROOF: " << add(i, i*i, 2L) << std::endl;
function<long ()> funky = [&]() -> long {
long V = i;
return add(V, V*V, 2L);
};
funks.push_back(funky);
}
for (auto&& x : funks) {
std::cout << x() << " ";
}
}
The output of running each x in funks is: [312, 312, 312 ... 312] corresponding to i = 13
However, I don't understand why this is the case, as I reinitialize V for each lambda, and the output should be [4, 12, 24, 40, ... 264]
It works when I change the capture clause to [=], but in my actual application the inputs will be quite large so I'd prefer to copy as few times as possible.
EDIT: I should clarify exactly what I'm looking for. I'd like to make a vector of N functions, [f_0, f_1, ... f_N], such that when calling f_i(), it calls F(V_i) for some large (known) function F and large V_i.
The reason I want to capture by reference is that I don't want to copy the V_i even once, but the result of my implementation is that every f_i() ends up calling F(V_N)
You are auto-capturing i by reference in your loop, but it's only a binding. The value is not actually used until after the loop, when calling the lambda. At that point, each call takes the captured "reference to i" (which is actually undefined behavior, given that i is no longer in scope), dereferences it and stores the value in V. You know the rest.
What is strange is that you are insisting on using references to integer values. It's likely the compiler is doing its best to inline these and just use plain copies, but you should consider that when you have a reference, you can often expect additional instructions to be generated to dereference that to a value. For primitive types, just copy.
Oh, and definitely capture i by value!!! As a matter of style, I prefer to be explicit about my captures:
function<long ()> funky = [i]() -> long {
long V = i;
return add(V, V*V, 2L);
};

Using lower_bound() with a set of objects in C++

I'm using a set of objects in C++ to get log(n) times for inserting and finding.
In the code below I'm able to insert elements and make them ordered by x attribute, however, I'm not able to use lower_bound to find the lower bounds based on the same attribute. I don't know how to fix that. Any help will be appreciated.
Most of the examples on sets that I could find were not about a set of objects
struct MyObject {
float x = 0;
float y = 0;
const bool operator < ( const MyObject &r ) const{
return ( x< r.x);
}
};
set<MyObject> nset;
int main(){
MyObject n1;
n1.x=5;
n1.y=1;
MyObject n2;
n2.x=3;
n2.y=2;
nset.insert(n1);
nset.insert(n2);
// this works, the elementes are sorted according to x
for(auto elem: nset){
cout << elem.x << endl;
}
// this doesn't work
set<MyObject>::iterator it = lower_bound(nset.begin(), nset.end(), 1.2);
cout << it->x << endl;
//neither this one
// set<MyObject>::iterator it = nset.lower_bound(1.2);
// cout << it->x << endl;
cout << "hello" << endl;
return 0;
}
I want the lower bound function to point me to the lower bound "x" in the set of objects but the code fails to compile. Compiler error to the first lower bound says: Invalid operands to binary expression ('const MyObject' and 'double')
Compiler error to the second lower bound says: No matching member function for call to 'lower_bound'
EDIT: while the answer provided by user: 1201ProgramAlarm was quite helpful for me to understand and fix the error. I still think it is more convenient in my case to have a lower_bound function that accepts floats rather than objects. So I have implemented the following function to help me achieve that. Copied below just in case others were interested:
set<MyObject>::iterator mylower_bound(set<MyObject> &myset, float val){
MyObject f;
f.x = val;
set<MyObject>::iterator it = myset.lower_bound(f);
return it;
}
nset stores MyObject objects, and lower_bound needs one of the thing stored in the set. You're passing it 1.2, which is a double, but there is no way to construct a MyObject from a double. Thus the compilation failure.
You'll need to pass a MyObject to nset.lower_bound to do your search.

Invalid range expression of std::valarray in range for loop

I have a simple usage of traversing a temporary std::valarray expression in range for loop, but got error: invalid range expression ...
main.cpp
#include <iostream>
#include <valarray>
int main()
{
std::valarray<int> xxx {2,7,1,8,2,8};
std::valarray<int> zzz {xxx};
for (auto x : xxx + zzz) std::cout << x << std::endl;
return 0;
}
clang++ main.cpp -std=c++11
main.cpp:10:17: error: invalid range expression of type 'std::__1::__val_expr<std::__1::_BinaryOp<std::__1::plus<int>, std::__1::valarray<int>, std::__1::valarray<int> > >'; no viable 'begin' function available
for (auto x : xxx + zzz) std::cout << x << std::endl;
^ ~~~
Is there really a good reason that it does not compile as I expected?
Return type of the overloaded operator+ is valarray<T>, so theoretically, value of the expression should be a temporary instance of type valarray<T>.
Synopsis:
template<class T> valarray<T> operator+ (const valarray<T>& x, const valarray<T>& y);
Version: Apple LLVM version 8.0.0 (clang-800.0.38) Target: x86_64-apple-darwin15.6.0
Note following line works
for (auto x : xxx += zzz) std::cout << x << std::end;
As a "begin" and "end" are available for the operator+ return type, namely valarray<T> I'd say the error is wrong and it should compile.
What do you want to do?
If you want to traverse xxx do the for only in it:
for (const auto x : xxx) std::cout << x << std::endl;
But answering the basics of your question, the expression (xxx + yyy) is not iterable. If you want to do the for loop in both, you do two fors:
for (auto x : xxx) std::cout << x << std::endl;
for (auto x : zzz) std::cout << x << std::endl;
If you want to do it in a single loop, you can append both
xxx += yyy;
for (auto x : xxx) std::cout << x << std::endl;
PD from edit: The line
for (auto x : xxx += yyy) std::cout << x << std::endl;
works because it makes the append first and then iterates. Is equivalent to my last suggestion. But (xxx+yyy) is not iterable.
From your comment:
valrray::operator+(valarray) does not exist.
valrray::operator+=(valarray) does exist.
The range for loop uses the non-member std::begin and std::end, which are not required to exist for the return type of operator+ for std::valarray.
The portable syntax is
for(auto x : std::valarray<int>(xxx + zzz))
This is noted on cppreference and in the standard under 26.7.1[valarray.syn]p4.1
This is a really good question. The reason is the behavior of A+B over valarrays.
Let us see what I mean by this.
First, try these lines.
std::valarray<int> xxx {2,7,1,8,2,8};
std::valarray<int> zzz {xxx};
auto t=xxx+zzz;
cout<<typeid(t).name()<<endl;
cout<<typeid(xxx).name()<<endl;
cout<<typeid(xxx+zzz).name()<<endl;
you will notice that they are not the same, and this is due to the definition of the + operator here https://en.cppreference.com/w/cpp/numeric/valarray/operator_arith3
As it is written the type is deduced. This means that its behavior is similar to auto. The question then is why does it not deduce the same type, valarray. Probably, it is due to a compiler optimization feature, or maybe it is a mistake, but it is clear that the reference does not force the compiler to deduce the same type. The thing is the deduced type happens to be not iterable by a range based for loop.
Please let me know if anything is not clear.

vector<bool>::operator[] misbehavior? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Why vector<bool>::reference doesn’t return reference to bool?
I used to think that with std::vector::operator[] we get deep copies of the accessed item, but it seems that it is not always true. At least, with vector<bool> the following test code gives a different result:
#include <iostream>
#include <vector>
using namespace std;
template <typename T>
void Test(const T& oldValue, const T& newValue, const char* message)
{
cout << message << '\n';
vector<T> v;
v.push_back(oldValue);
cout << " before: v[0] = " << v[0] << '\n';
// Should be a deep-copy (?)
auto x = v[0];
x = newValue;
cout << " after: v[0] = " << v[0] << '\n';
cout << "-------------------------------\n";
}
int main()
{
Test<int>(10, 20, "Testing vector<int>");
Test<double>(3.14, 6.28, "Testing vector<double>");
Test<bool>(true, false, "Testing vector<bool>");
}
Output (source code compiled with VC10/VS2010 SP1):
Testing vector<int>
before: v[0] = 10
after: v[0] = 10
-------------------------------
Testing vector<double>
before: v[0] = 3.14
after: v[0] = 3.14
-------------------------------
Testing vector<bool>
before: v[0] = 1
after: v[0] = 0
-------------------------------
I would have expected that v[0] after the x = newValue assignment would still be equal to its previous value, but this seems not true.
Why is that?
Why is vector<bool> special?
vector<bool> is a hideous abomination and special. The Committee specialized it to pack bits, therefore it does not support proper reference semantics, as you cannot refer to a bit, this means that it has a non-conforming interface and does not actually qualify as a Standard Container. The solution that most people use is simply to never, ever, use vector<bool>.
vector<bool>::operator[] neither yields a bool nor a reference to a bool. It just returns a little proxy object that acts like a reference. This is because there are no references to single bits and vector<bool> actually stores the bools in a compressed way. So by using auto you just created a copy of that reference-like object. The problem is that C++ does not know that this object acts as a reference. You have to force the "decay to a value" here by replacing auto with T.
operator[] returns a T& for every value of T except for bool, where it gives a reference proxy. See this old column by Herb Sutter on why using vector<bool> in generic code is bad idea (and why it is not even a container). There is also a special Item about it in Effective STL by Scott Meyers, and tons of questions on it here at SO.