C++ range-based for loop over valarray rvalue is not working
Asked Answered
C

3

15

I would like to iterate over a temporary valarray, but it isn't working. Here is my (non-working) code:

#include <iostream>
#include <valarray>
int main()
{
        using namespace std;
        valarray<int> numerators = {99, 26, 25};
        valarray<int> denominators = {9, 2, 5};
        for (int i : numerators / denominators) { cout <<  i << ","; }
        // lots of errors
        return 0;
}

Below is a minimal working example of what I would like to achieve, except that I don't want to define an object like temp_array.

#include <iostream>
#include <valarray>
int main()
{
        using namespace std;
        valarray<int> numerators = {99, 26, 25};
        valarray<int> denominators = {9, 2, 5};
        valarray<int> && temp_array = numerators / denominators;
        for (int i : temp_array) { cout << i << ","; }
        // prints 11,13,5,
        return 0;
}

My compiler is g++ version 4.8.5 (Red Hat 4.8.5-4). I am compiling with the -std=c++0x flag.

I've tried other syntax such as for (auto&& i : temp_array) and for (int const & i : temp_array), but it doesn't work.

Condyloma answered 28/1, 2016 at 16:39 Comment(3)
valarray's operator/ is allowed to return a proxy object a la expression templates.Agrarian
I've been away from C++ for too long, apparently. Could someone explain how for(int i : temp_array){} is a valid for loop statement? Shouldn't it be something like for(init;end_condition;increment)?Slumlord
@Slumlord See C++11's range-based for loop.Carmoncarmona
L
11

As pointed out in @Yam Marcovivc's answer the operation result isn't guaranteed to be a std::valarray<int> that can be passed directly to std::begin(). A temporary constructed object does the trick:

#include <iostream>
#include <valarray>
int main()
{
        using namespace std;
        valarray<int> numerators = {99, 26, 25};
        valarray<int> denominators = {9, 2, 5};
        for (int i : valarray<int>(numerators / denominators)) { 
            cout <<  i << ","; 
        }
        return 0;
}

See a Live Demo

Lucic answered 28/1, 2016 at 16:52 Comment(0)
D
18

From the documentation (which also includes the official way to do this in a single expression):

Unlike other functions that take std::valarray arguments, begin() cannot accept the replacement types (such as the types produced by expression templates) that may be returned from expressions involving valarrays: std::begin(v1 + v2) is not portable, std::begin(std::valarray(v1 + v2)) has to be used instead.

The intent of this function is to allow range for loops to work with valarrays, not to provide container semantics.

As to what might be the reason, there's also this (which was pointed out by @chris):

[arithmetic operators] can be implemented with the return type different from std::valarray.

So there's technically nothing to guarantee that what returns can safely be passed on to std::begin.

Dendrology answered 28/1, 2016 at 16:48 Comment(0)
L
11

As pointed out in @Yam Marcovivc's answer the operation result isn't guaranteed to be a std::valarray<int> that can be passed directly to std::begin(). A temporary constructed object does the trick:

#include <iostream>
#include <valarray>
int main()
{
        using namespace std;
        valarray<int> numerators = {99, 26, 25};
        valarray<int> denominators = {9, 2, 5};
        for (int i : valarray<int>(numerators / denominators)) { 
            cout <<  i << ","; 
        }
        return 0;
}

See a Live Demo

Lucic answered 28/1, 2016 at 16:52 Comment(0)
E
3
    for (int i : (valarray<int> &&)(numerators / denominators)) { cout << i << ","; }
Estelleesten answered 28/1, 2016 at 16:50 Comment(1)
Works, but recommending c-style casts might not be the best advice. Also just posting code without any further explanation (and be it referring to other answers) doesn't make this a great answer.Mastectomy

© 2022 - 2024 — McMap. All rights reserved.