Link to home
Start Free TrialLog in
Avatar of phoffric
phoffric

asked on

C++ Valarray program compiler error using g++

The below program builds/runs OK in VS 2010 Express, but since we run on Linux, I moved it over. I get a compiler error using g++ version 4.8.2. Not sure what I have to do to get the program to run on Linux. (BTW, my project does not use C++11.)
#include <valarray>
using namespace std;

void foo(valarray<int>& va)
{
   slice_array<int>& sa = va[slice(0,3,2)];
   sa = 98;
}

int main()
{
   valarray<int> valary(21);
   for(size_t i=0; i<valary.size(); ++i)
   {
      valary[i] = i;
   }
   foo(valary);
}

Open in new window

$ g++ slice_array.cpp
slice_array.cpp: In function ‘void foo(std::valarray<int>&)’:
slice_array.cpp:6:42: error: invalid initialization of non-const reference of type ‘std::slice_array<int>&’ from an rvalue of type ‘std::slice_array<int>’
    slice_array<int>& sa = va[slice(0,3,2)];
                                          ^

Open in new window

Avatar of sarabande
sarabande
Flag of Luxembourg image

invalid initialization of non-const reference of type ‘std::slice_array<int>&’ from an rvalue of type ‘std::slice_array<int>’
the g++ returns an rvalue when you call valarray operator[]. because of that you may not use a non-const reference variable. actually operator[] should have two prototypes, one returning a const reference to the "element" and one a writeable reference. the problem here is that operator[] returns by value and not by reference. the slice object returned therefore is a temporary which the g++ provides as rvalue. i don't know whether one could call it a bug, but probably it shouldn't actually make a difference when using it.

Not sure what I have to do to get the program to run on Linux.
did you try to use a const reference variable. a slice neither can be constructed nor be copied. because of that all public member functions actually should be const anyway.

Sara
Avatar of phoffric
phoffric

ASKER

As noted in the OP, VS 2010 Express built the above program and it ran as expected. Not sure why that is. Could it be running C++11 instead of C++03? Can I force it to run C++03 as that is what my project uses?

My book says:
slice_array<T> operator[](slice); // references to elements
A slice_array can be copied.
A book from 1997 does say that that slice_arrays cannot be copied, but it does have code similar to the OP code (without a need for adding a const).

I would like VS 2010 Express to behave the same way as g++.
(Be back in 12 hrs.)
SOLUTION
Avatar of jkr
jkr
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
My book says:
slice_array<T> operator[](slice); // references to elements

from syntax slice_array<T> is an object and not a reference. internally it could be a reference (pointer) nevertheless.
the msdn reference says:

The class describes an object that stores a reference to an object of class valarray<Type>, along with an object of class slice, which describes the sequence of elements to select from the valarray<Type> object.

The template class is created indirectly by certain valarray operations and cannot be used directly in the program. An internal, auxiliary template class that is used by the slice subscript operator:

slice_array<Type> valarray<Type::operator[] (slice).

You construct a slice_array<Type> object only by writing an expression of the form va[sl], for a slice sl of valarray va. The member functions of class slice_array then behave like the corresponding function signatures defined for valarray<Type>, except that only the sequence of selected elements is affected.


the visual c++ creates a new temporary object slice_array which therefore can be referenced as lvalue.

the standard reference (http://www.cplusplus.com/reference/valarray/valarray/operator%5B%5D/) says (regarding valarray operator[])

valarray<T> operator[] (slice slc) const;
slice_array<T> operator[] (slice slc);
...
The subscript access versions (2) return a sub-array object that selects the elements specified by its argument:
- If the valarray is const-qualified, the function returns a new valarray object with a copy of this selection.
- Otherwise, the function returns a sub-array object, which has reference semantics to the original array, ready to be used as an l-value.

you see the visual c++ implementation is more compliant to the (both C98 and C++11) standard as it is with g++.

'A slice_array can be copied' versus slice_arrays cannot be copied
in the msdn i read somewhere the statement that slice_array objects cannot be copied.  since you can prevent from copying by defining a private copy constructor and a private operator= it is rather simple. but actually i didn't see a reson why copies should be forbidden. obviously you could create copies anyhow by using the expression va[sl] twice.

the following code compiles (visual studio 2010):

    std::valarray<int> va(3);
    std::slice sl(0,2, 2);
    std::slice_array<int> sa1 = va[sl];
    std::slice_array<int> sa2 = sa1;

Open in new window

what proves that the copy constructor of slice_array was working.

I would like VS 2010 Express to behave the same way as g++.
as told the behavior of vc compiler is compliant to the standard while g++ seems to support valarray operator[] const only and returns an rvalue. i wonder whether g++ allows to create copies of slice_array. if so, you probably should go this way for both platforms rather than to use local references to va[sl]. where the behavior is different.

Sara
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Also note that if you are using C++ 11 you can simplify your code to just the following:

auto const & sa = va[slice(0,3,2)]; // bind to const ref

Open in new window


or

auto && sa = va[slice(0,3,2)]; //; bind to r-value ref

Open in new window


or

auto sa = va[slice(0,3,2)]; //; take a copy of the returned value

Open in new window


http://en.cppreference.com/w/cpp/language/auto
>> as told the behavior of vc compiler is compliant to the standard
Oh, if only it was :(
returns an r-value (a temporary object in this case).

actually the standard doesn't support this assertment. a temporary object and a rvalue are two different things. an rvalue only could be used for the right side of an assignment. a temporary object is an lvalue within the current scope.

the docs for slice_array didn't change from C++98 to C++11. and you can find the following at http://www.cplusplus.com/reference/valarray/valarray/operator%5B%5D/ 

Otherwise, the function returns a sub-array object, which has reference semantics to the original array, ready to be used as an l-value.
what obviously is in contradiction to the visual c++ compiler verdict evilrix has posted.

note, the main purpose of a slice_array object is to provide a comfortable filter to a valarray. the only way to get a slice_array is via valarray operator[slice]. we can recognize a lot of bewilderment - unfortunately also here in this thread - regarding the nature of an object returned by value. but there is no doubts even if it is a temporary, an object returned by value is not an rvalue. if we accept this, g++ obviously doesn't return a temporary helper object but a const reference to either slice_array or valarray what is as far as i can see a contradiction to that what the standard says.

Sara
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
The only way to preserve it is to bind it to a const l-value reference, an r-value reference or take a copy.

again, it is a different thing that a temporary would not "preserve" if you don't bind it to a const reference or take a copy to the fact that it could be used as a lvalue. so the slice_array object returned by va[sl] is a temporary but also can be used as lvalue, what means that you could apply any non-const member function or non-const operator directly by statements like va[sl] *= 10; what apparently could not work if va[sl] was an rvalue.

or:

class X
{
   int m;
 public:
   X(int i) : m(i) {}
   static X GetX(int k) { return X(k); }
   X& operator=(int n) { m = n; return *this; }
   friend std::ostream & operator<<(std::ostream & os, const X & x) { os << x.m; return os; }
};

void f()
{
      std::cout << (X::GetX(999) = 5) << std::endl;
}

Open in new window


rvalue or lvalue?

Sara
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks for this discussion. I appreciate that. I got home much later than expected and now it is past midnight. I will spend more time on this (and I do have Friday off - maybe even Thursday if I get more time in tomorrow along with some success!).

Sorry to be so brief in what should be a more detailed engagement. :(
@jkr - I ran your 2nd program on both VS 2010 Express and g++ to confirm the results. Thanks. It worked without needing const and even without an extra temp. At work I will eventually be doing timing tests. I don't think using a slice_array object will cause much of an issue as long as the reference semantics hold when referring to the valarray object. I use VS 2010 instead of 2013 because I was concerned that C++11 goodies might creep in, and my project is not C++11. (Neither is it pure C++11, because I accidentally found that I could define on the stack an array (even 2d) whose dimension lengths are passed in as function arguments - worked with g++ as well as Intel compilers.)

@sara - your suggested program does work, but I was trying to get my book's style working, and it looks like jkr cracked the l-value, r-value issue (at least as a work-around without introducing const). I believe that "sa" in the OP code is a reference to a slice_array object and that its usage provides reference semantics to the underlying valarray. That was my hope. The intention was to be able to refer to the original valarray object elements in the slice without having to actually copy the slice over to another valarray object. (This exercise is a prelude to using gslice_array on valarrays that are very large, and I would like to try to avoid copying sub-matrix.)

I believe the answer to your question as to why certain operations are prohibited has to do with compiler optimizations that can be performed if the compiler knows that no aliasing will occur. When you seemed to imply that my book was wrong with regards to reference semantics, I started to look at the C++03 standard (and that's why I use the book to avoid that). But that leads to ...

@rx - Glad you joined in because I am hoping to understand what the standard really is saying now that I started looking at it. My post is a simplified version of the book's code (which is already pretty simple), so I wasn't surprised when VS 2010 Express worked. Book is BS's 4th edition, but 3rd special edition has same sample code. Still trying to figure out whether one compiler has a bug, or whether there needs to be flags set to make them strictly conforming to C++03. My boss (and I) should know whether I am producing pure conforming code.

BTW - in the OP I noted that C++11 is not available on my project, so I hope to continue this discussion in a C++03 vein. At work today, I tried using our actual Intel compiler and got the same rvalue error. So, I am still confused as to which compilers are conforming. (I used to think I understood rvalue/lvalue in my C days a long time ago.)

@all:
fyi - The lead asked me to try to come up with a high performance method of using valarray because Intel compiler has flags to enable SIMD when using it. He apparently wasn't happy with previous attempts to make use of it. (And I didn't see any slice_array or gslice_array in their classes, so I am guessing that may be a source of the performance hit due to copying and temporaries being created - just a WAG at this point.) From my other valarray question, you can see that I learned how to create a sub-matrix using gslice_array. But I think there may be copy operations going on that I hope to avoid.

I find it odd that various online resources seem to say opposite things about slice_array w.r.t. the ability to use it explicitly in a program. Yet, jkr's solution shows working programs that explicitly mention slice_arrays and they work on at least two compilers.

Thanks all. Will read all of this in more detail in a few days (or a little at a time).
evilrix, i hope you pardon me if i don't want to dive into the swamp of rvalue, lvalue, prvalue, xvalue, since i am not a compiler expert and much less experienced in studying the secrets of describing a standard than you. i reread all the comments and quotes you made and think i have found a sufficient explanation (sufficient for me) of the difference between the two compilers regarding the temporary slice_array: the difference is the lifetime of the temporary where the standard only guarantees that it is preserved until end-of-scope if it either was copied or bound to a const reference while visual c++ compiler also holds the temporary alive if you bind it to a non-const reference.

it looks like jkr cracked the l-value, r-value issue (at least as a work-around without introducing const).

phoffric, jkr's code made a copy of the temporary slice_array and then an alias to that local copy. since we already knew that copying of slice_array objects was allowed and because taking a copy of a temporary is a valid means to extend the lifetime of the temporary until end-of-scope, the workaround is safe.  

I believe that "sa" in the OP code is a reference to a slice_array object and that its usage provides reference semantics to the underlying valarray. That was my hope.
a slice_array doesn't store own values but has references to valarray elements. in any case the slice_array allows manipulation of the original values within its lifetime. because of that the following code also should work (for both compilers):

void foo(valarray<int>& va)
{
   va[slice(0,3,2)] = 98;
}

Open in new window


but also may confuse again if we think about the following assertments made:

All "rvalue" means is that it cannot appear on the left hand side of an assignment.
with the deepest respect, you are wrong!
(I used to think I understood rvalue/lvalue in my C days a long time ago.)
perhaps we come to the conclusion that all these statements stretch the truth :-)

Sara
Here is the code from The C++ Programming Language, 4th Edition by Bjarne Stroustrup (and I think the function is the same as in the 3rd Special Edition).
A user cannot directly create a slice_array. Instead, the user subscripts a valarray to create a slice_array for a given slice. Once the slice_array is initialized, all references to it indirectly go to the valarray for which it is created. For example, we can create something that represents every second element of an array like this:
void f(valarray<double>& d)
{
  slice_array<double>& v_even = d[slice(0,d.size()/2+d.size()%2,2)];
  slice_array<double>& v_odd = d[slice(1,d.size()/2,2)];
  v_even *= v_odd; // multiply element pairs and store results in even elements
  v_odd = 0; // assign 0 to every odd element of d
}

Open in new window

This code builds/runs well on VS 2010 Express. So, I naively thought that VS was correct and that something was wrong with g++, and was hoping that maybe a g++ flag would set things straight so that I can write one piece of code that works the same on both platforms.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I see that there were some errata to The Programming Language on the BS code; yet attempts to get them into later releases failed. Other online searches seems to suggest that VS 2010 allows non-portable C++ code (on the topic of referencing rvalues) to be built. There is a semantic debate as to whether VS is a non-compliant compiler which builds all correct code, but allows extensions (so as to be non-portable).

Right now, I believe that g++ is giving the correct error, and VS (perhaps due to default config settings) is not.

I will check tomorrow if the /Za switch is set to "Disable Language Extensions" to see if I can force VS to give an error.
https://msdn.microsoft.com/en-us/library/0k0w269d.aspx
Gcc is giving the correct error.  You cannot bind an rvalue to an lvalue reference. This is why rvalue references were introduced in C++11. VS2010 is wholly non-standard compliant. Later versions are better, but still take liberties.

If you can't use C++11 (better rethink this as C++14 will be the standard soon)  your only choice is to take a copy of the returned value. Since the object is designed to have reference semantics to the original array, this is perfectly correct, if slightly inefficient.
>> I used to think I understood rvalue/lvalue in my C days a long time ago
Actually, there's nothing super magical about either.

lvalues are entities that may exist on the left (and right) hand side of an assignment.
rvalues may only appear on the right hand side of an assignment.

That's it. There is nothing else specifically prescribed to either. For example, an lvalue, by definition, can't be const but an rvalue doesn't have to be. The C++11 standard expands on these definitions and introduces xvalues, gvalues and pure rvalues, but these are just refinements of these two basic types.

My advise is to not worry too much about what lvalue or rvalue means, just remember that a temporary (and when a function returns by value it always creates a temporary*) can never be bound to a non-const C++03 style reference.

* The C++03 standard allows for Return Value Optimisation; however, the semantics of the result being an lvalue do not change. Further, C++11 introduced rvalue references, and by default temporary values are returned using move rather than copy semantics. Again, the result is still an rvalue!

>> evilrix, i hope you pardon me if i don't want to dive into the swamp of rvalue, lvalue, prvalue, xvalue
Truthfully, I don't blame you. The standard document can be hard to read and even harder to follow. It was written by very smart people to be a guide for compiler writers. It is the final point of call when arbitrating a confusion with the language but that doesn't mean it is the best place to go (at least, not unless you want you brain frazzled). I often find myself reading the same paragraph over and over and over and, each time, thinking it is telling me something different. Fortunately, on this particular subject I find it to be pretty clear (at least, I think I do - heh).
Gcc is giving the correct error.  
phoffric, if the c++ code you posted is from a Stroustrup book, then Stroustrup is not using gcc, obviously.

You cannot bind an rvalue to an lvalue reference.
if the temporary object returned from valarray operator[] is an rvalue, why statements like va[sl] *= 10; would compile? why does the reference for valarray at cplusplus.com state "valarray::operator[slice] returns a sub-array object, which has reference semantics to the original array, ready to be used as an l-value"? evilrix gave an answer to this when he explained "operators are also just functions". hence, assignment operators also are functions and therefore you also can use the return value as lvalue, like in 'va[sl] = 123;' since that could be expressed as 'va[sl].operator=(123);' on the other hand is "slice_array & x = va[sl];' invalid since it is an initialization and not a function. that means, when the temporary was used for an initialization or was passed by reference to another function, it turns to an rvalue (in its last modified state) and some compilers would have an error if you do so.

your only choice is to take a copy of the returned value.
since you can modifiy the returned object directly, a copy solves the issue and improves readability but is not the "only" way.

i tried to understand all the links and statements evilrix has posted regarding the rvalue status of a temporary. i think he is right that the current standard doesn't allow to make an alias (a reference variable) of a temporary returned by a function. the reason for this may be due to the factor that container elements may be "moved" rather than be copied such that the lifetime of the temporary must end with the current statement (and the initialization of an alias already is the next statement). i wonder though why a const reference still is valid until end-of-scope though of course manipulating the underlying valarray also could make those variables corrupt or point to wrong elements.

Sara
It's returns an object with reference  semantics. Think of it as a smart pointer object but rather than having pointer semantic it has reference semantics. In other words, it returns an object by value but that object references the original array.
A temporary bound to a reference has the same lifetime of the reference to which it is bound. Why?  Because that is that the standard prescribes.
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thank you for your detailed responses.