I'm wondering if the following is valid:
#include <iostream>
#include <vector>
std::vector<int>& getVec()
{
static std::vector<int> vec{1, 2, 3, 4, 5};
return vec;
}
int main()
{
for (const auto& i : getVec())
{
std::cout << "i = " << i << std::endl;
}
return 0;
}
Basically I'm unsure about the lifetime of the temporary from getVec(). I've looked at both this post and this one, but am in a bit of a different situation as my function returns a reference to static data. Specifically, I'm wondering if scenario violates the following exception in the stated rule:
A temporary bound to a reference parameter in a function call [...]
or if this is indeed safe code. I think it is safe, but just wanted to be sure.
Yes, this is fully valid and well-defined.
The range-based for loop in your question is defined to be equivilent to the following, for imaginary variables range, begin, and end:
auto&& range = getVec();
auto begin = std::begin(range);
auto end = std::end(range);
for (; begin != end; ++begin)
{
const auto& i = *begin;
{
std::cout << "i = " << i << std::endl;
}
}
After reference collapsing rules are applied, the type of range becomes std::vector<int>&. That means no temporaries are ever created. The loop iterates over the static vector defined in getVec.
If getVec instead returned by value, the type of range would be std::vector<int>&&, and lifetime extension would be applied. That would extend the lifetime of the temporary object to that of the reference, and everything would still be totally valid.
Related
I am trying to loop through an array of functions stored in a vector, and i want to call each of them by the iterator pointer object,
but something like this:
itr->funcs[i](5); // call the ith index function && pass int 5 as param
is not the solution i guess, what is the solution then ?
Below is my code, please check the last for loop in the code.
#include <iostream>
#include <string>
#include <vector>
// to be able to take other functions as arguments in a function
#include <functional>
using namespace std;
// receive other functions inside a function
// syntax: return_type function_name(std::function<return_type(parameter_lists)>func_name, parameter_lists)
double doMath(std::function<double(double)> someOtherFunction, double num){
return someOtherFunction(num);
}
double multBy2(double d){
// b'coz x is pointing to multBy2 the word 'someOtherFunction' in the
// above function behaves like an alias for this function 'multBy2'
return d * 2;
}
double multBy3(double d){
return d * 3;
}
int main (){
// dec && init
auto x = multBy2; // x pointing to multBy2 now
std::cout << "2 * 5.1 : " << x(5.1) << "\n";
std::cout << "multBy2 will be called: " << doMath(x, 6.1) << "\n";
std::cout << "multBy2 will be called, again: " << doMath(x, 6.1) << "\n";
// store different functions inside a vector
// you must use the same signature type functions
std::vector<function<double(double)>> funcs(2);
funcs[0] = multBy2; // multBy2 stored at the zeroth index
funcs[1] = multBy3; // multBy3 stored at the first index
// check
// funcs[0](10), pass value by index
std::cout << "2 * 10 = " << funcs[0](10) << "\n";
std::cout << "3 * 10 = " << funcs[1](10) << "\n";
// loop through them
for (auto itr = funcs.begin(); itr != funcs.end(); itr++){
// iterate through the functions
// itr->funcs[1](10); // I KNOW THIS IS WRONG, WHAT SHOULD I WRITE HERE?
}
return 0;
}
The variable itr is an iterator, which is basically a pointer, i.e. they both point to to an object. In this case the iterator is pointing to a function. You can get to the function by dereferencing itr (much like a pointer) with *itr. Then you can use this object like it's a function (because it is one):
for (auto itr = funcs.begin(); itr != funcs.end(); ++itr)
(*itr)(10); // like calling func[i](10)
Since it's an iterator, you might also want to use the -> operator, so that you can use the pointed at object directly. Unfortunately, if you tried to do what might seem like the obvious thing:
itr->(10); // error
the syntax of the language just doesn't allow it (parentheses can be tricky to figure out, even for a compiler). Fortunately, the language does have a way of allowing you to explicitly say what you mean, which is, "I just want to treat it like a function, so I can call it with ()". There is a function called the call operator which is spelled operator() that you can use. The syntax for that is:
for (auto itr = funcs.begin(); itr != funcs.end(); ++itr)
itr->operator()(10);
However, this syntax does seem to defeat the purpose of using the convenient -> operator in the first place.
However, I recommend that you use a range-for loop here (it's usually the better choice if you can use it). The syntax is much clearer at expressing the intent of the code:
for (auto &func : funcs) // for every function in funcs
func(10); // use the function
Here's a working demo.
I would like to understand the problem with the following code. It does compile but it doesn´t work as expected.
#include <iostream>
#include <vector>
class A
{
public:
std::vector<int> getVector() { return m_vector; }
std::vector<int> m_vector = {1, 2, 3};
};
int main()
{
A objA;
for(int i = 0; i < objA.getVector().size(); i++)
{
int &item = objA.getVector().at(i);
std::cout << "\nvector item: " << item;
}
return 0;
}
Output:
vector item: 0
vector item: 0
vector item: 3
Expected output:
vector item: 1
vector item: 2
vector item: 3
I understand that there is no need to declare the item as a reference but I would like to understand the problem that arises after doing it this way.
I think that the problem is that the reference item expects a lvalue and method std::vector::at returns a reference. As the return type of method std::vector::at is int&, the compilation doesn´t throw any error but then the result is not as expected.
Would it be possible to confirm if this is the problem and I´m not missing anything else?
getVector() returns a std::vector by-value. Therefore objA.getVector().at(i) is calling .at(i) on a temporary object.
.at returns a reference to the element at the given position. int &item is bound to this referenced element of the temporary object.
After the line
int &item = objA.getVector().at(i);
the temporary std::vector is destroyed and with it the element that item is referencing.
So using that reference then in
std::cout << "\nvector item: " << item;
causes undefined behavior, because the object that item was referencing doesn't exist anymore.
This can be fixed by returning by-reference from getVector, in which case item will refer to the element in the std::vector member of objA and not a temporary copy of it.
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);
};
I haven't found a question that answers the part I'm confused on, and I apologize if someone did answer it.
I'm confused on whats going on in this for-loop, how is it looping through the addresses?
int arr[] = { 1, 2, 3, 4, 5 };
for(const int &arrEntry : arr) {
cout << arrEntry << " ";
}
Perhaps the placement of & is causing confusion. Remember that C++ doesn't care where you put spaces. Since this: for (const int &arrEntry : arr) is a declaration of a new variable arrEntry for use inside the loop, the use of & on the left-hand side of its name means we are defining an object that has a reference type, specifically arrEntry is a reference to a const int. This means that within the loop, arrEntry is not a copy of the data you're looping over, only a reference to it. The const means that you can't change its value.
If this were not a declaration, and if arrEntry were defined previously, then the expression &arrEntry would indeed be taking the address of arrEntry. Within the body of the loop, arrEntry is already defined, and so you can take its address with &arrEntry
int arr[] = { 1, 2, 3, 4, 5} ;
for(const int &arrEntry : arr){
cout << arrEntry << " "; // prints a const int
cout << &arrEntry << " "; // prints a pointer to a const int
}
The range-based for loop in C++ is actually just syntactic sugar that is equivalent to the following (provided by cppreference:
for (range_declaration : range_expression) loop_statement;
// is equivalent to:
{
auto && __range = range_expression ;
for (auto __begin = begin_expr, __end = end_expr; __begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
In the above code block, begin_expr and end_expr are equivalent to std::begin(__range) and std::end(__range) respectively.
So in the case of using const int &arrEntry, arrEntry is actually declared inside the "real" (normal) for loop and thus in each iteration it refers to a different object in the range, as if by using the raw iterators directly.
Note that this would not be possible if arrEntry was declared outside the normal for loop, as references cannot be repointed to refer to a different object.
Another important (side) fact to consider is that range_expression is kept alive for the entire duration of the loop, which means you can use a prvalue there (e.g. calling a function that returns a std::vector<int> by value.
In your code, the &arrEntry is a reference to arr. This is implicit in the Ranged based For-Loop.
for(const int &arrEntry : arr){
cout << arrEntry << " ";
}
You could do it without the reference, the result is the same.
But notice the value of arr is copied to arrEntry.
for(const int arrEntry : arr){
cout << arrEntry << " ";
}
recently I've encountered very very peculiar question when using auto in C++, just ... just look at the following code snippet :
my main function:
#include <list>
#include <iostream>
#include <stdio.h>
int main(){
int a = 10, b = 20, c = 30;
list<int> what;
what.push_back(a);
what.push_back(b);
what.push_back(c);
read(what);
return 0;
}
And here's function read:
void read(const list<int>& con){
for (auto it : con){
printf("%p\n", &it);
cout << it << endl;
}
return ;
}
And here's is the output :
0x7fffefff66a4
10
0x7fffefff66a4
20
0x7fffefff66a4
30
What the heck is that? Same address with different content !?
And more strange this is, if I modify the for-loop by adding an '&'
that is:
for (auto& it : con){
All the output makes sense immediately, the addresses would change by iteration
So my question is,
Why does the '&' sign make a change under this circumstance?
for (auto it : con){
Same address with different content !?
This is very typical for variables with automatic storage duration. This has nothing to do with auto in C++†. You would get the same result if you had used int:
for (int it : con){
The lifetime of it (as well as each automatic variable within the loop) is just a single iteration. Since the lifetime of the it in last iteration was ended, the next iteration can re-use the same memory, and that's why the address is the same.
Why does the '&' sign make a change under this circumstance?
Because T& declares a reference to type T. Reference variables are different from non-references (object variables). Instead of holding a value such as an object would, a reference instead "refers" to another object.
When you use the addressof operator on a reference, the result will be the address of the referred object; not the address of the reference (which might not even have an address, since it's not an object). That is why the address changes in the latter case. In this case, the references would refer to the int objects that are stored in the nodes of what (because con itself is a reference, and refers to the passed object).
† I mention in C++, because in C auto is in fact a storage class modifier that signifies automatic storage class. It has never had that meaning in standard C++, and its use obsolete in C as well. It's a vestigial keyword from the B language.
In C++, auto declares a type that will be deduced from context.
let's see the expanded version of the : loop syntax first.
for( auto it: container) {
...
}
is conceptually the same as
for( auto _it = container.begin(); _it != container.end(); it++) {
auto it = *_it;
...
}
while the reference form:
for( auto& it: container)
is the same as
for( auto _it = container.begin(); _it != container.end(); it++) {
auto &it = *_it;
...
}
So in the first case it is a copy of the items in the container, in the second case it is a (lvalue) reference of it, hence if you modify it in the second loop it affects the items in the container
The address issue too can be explained this way: in the copy example the local variable has always the same address in each loop iteration (because their lifetime do not overlap, the compiler has no reason not to use the same address in the stack), thought if you factorize the code inside a function you may observe it changing in different function invocation (because the stack size might be different), in the reference example the address is different every time, because taking the address of a reference will yield the address of the object being referenced (in this case, the item in the container)
Note that auto is standing in for int in your case. So it's a red herring. Consider
for (int i = 0; i < 10; ++i){
int j = i;
cout << (void*)&j << '\n';
}
Since j has automatic storage duration, it is most likely created each time with the same address - but points to a different value - , j is being pushed then popped from a stack on each iteration (let's set aside compiler optimisations). That is what is happening in your case with for (auto it : con){. it has automatic storage duration.
When you write
for (auto& it : con){
it is a reference to an int within the container con, so its address will differ on each iteration.