Topic: Constness of return-by-value temporaries


Author: Bob Hairgrove <invalid@bigfoot.com>
Date: Mon, 8 May 2006 17:08:00 CST
Raw View
On Mon,  8 May 2006 10:37:38 CST, "Tom   s" <NULL@NULL.NULL> wrote:

>
>It's perfectly legitimate to use "const_cast", and subsequently alter an
>object, but only if the object was defined as non-const in the first place.
>Example 1:
>
>#include <string>
>
>void Func( const std::string &cstr )
>{
>    std::string &str = const_cast<std::string&>(cstr);
>
>    str += "monkey";
>}
>
>int main()
>{
>    std::string str("ape");
>
>    Func(str);
>}
>
>
>
>I've seen many examples of where a temporary object is non-const. Example 2:
>
>
>#include <string>
>#include <vector>
>
>std::vector<std::string> Func()
>{
>    std::vector<std::string> vec;
>
>    vec.push_back("One");
>    vec.push_back("Two");
>
>    return vec;
>}
>
>int main()
>{
>    Func().push_back("Three"); // <-- Non-const member function
>}
>
>
>(I'll admit that I'm not certain if ALL temporaries are non-const throughout
>the entire language. Could someone please clarify that?)

Since it is possible to return either a const OR non-const object from
a function, I would say this is not true. However, AFAIK, all
temporaries returned from a function are rvalues, whether or not they
are const (NOTE: const objects which are not temporaries are lvalues
because you can take their address. I believe that this is one of the
ways to distinguish between the two, although I am not 100% sure --
and I don't have time to look up chapter and verse in the C++ standard
bible at the moment. ;)

>>We can use a const reference to "extend the lifetime" of a temporary
>returned-by-value from a function. Example 3:
>
>#include <string>
>#include <vector>
>
>std::vector<std::string> Func()
>{
>    std::vector<std::string> vec;
>
>    vec.push_back("One");
>    vec.push_back("Two");
>
>    return vec;
>}
>
>int main()
>{
>    const std::vector<std::string> &cvec = Func();
>
>    std::string str( cvec.at(0) );
>
>    str += "monkey";
>}
>
>We saw in Example 2, that the temporary object returned-by-value from
>"Func" was non-const.

But it is nevertheless an rvalue...

>I have a question, which I have asked on comp.lang.c++, but which no-one has
>seemed willing to answer. Is the following translation unit well-formed, and
>absent of undefined behaviour?
>
>#include <string>
>using std::string;
>
>#include <vector>
>using std::vector;
>
>vector<string> Func()
>{
>    vector<string> vec;
>
>    vec.push_back("One");
>    vec.push_back("Two");
>
>    return vec;
>}
>
>#include <iostream>
>using std::cout; using std::endl;

You need to inclue <ostream> as well in order to conform to the
standard if you are using std::endl.

>
>int main()
>{
>    const vector<string> &cvec = Func();
>
>    vector<string> &vec = const_cast< vector<string>& >(cvec);
>
>    vec.push_back("Three");
>
>    cout << vec.at(0) << endl
>         << vec.at(1) << endl
>         << vec.at(2) << endl;
>}
>
>
>
>-Tom   s

const_cast *can* invoke undefined behavior. Did you read 5.2.11 very
carefully?

As I see it, the problem is that all the code within the main()
function must be seen in light of the fact that Func() might just as
well return a const reference, in which case the two const_casts
*would* invoke UDB.

--
Bob Hairgrove
NoSpamPlease@Home.com

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Tom Widmer <tom_usenet@hotmail.com>
Date: Mon, 8 May 2006 22:09:41 CST
Raw View
Tom   s wrote:
> It's perfectly legitimate to use "const_cast", and subsequently alter an
> object, but only if the object was defined as non-const in the first place.

> (I'll admit that I'm not certain if ALL temporaries are non-const throughout
> the entire language. Could someone please clarify that?)

Temporaries may be const or non-const. e.g. this creates a const temporary:

typedef const int cint;
int i = cint(5);

Const temporaries are of dubious utility though, and can prevent
optimization of copies of temporaries.

> I have a question, which I have asked on comp.lang.c++, but which no-one has
> seemed willing to answer. Is the following translation unit well-formed, and
> absent of undefined behaviour?
>
> #include <string>
> using std::string;
>
> #include <vector>
> using std::vector;
>
> vector<string> Func()
> {
>     vector<string> vec;
>
>     vec.push_back("One");
>     vec.push_back("Two");
>
>     return vec;
> }
>
> #include <iostream>
> using std::cout; using std::endl;
>
> int main()
> {
>     const vector<string> &cvec = Func();
>
>     vector<string> &vec = const_cast< vector<string>& >(cvec);
>
>     vec.push_back("Three");
>
>     cout << vec.at(0) << endl
>          << vec.at(1) << endl
>          << vec.at(2) << endl;
> }

It may or may not be fine. The problem is that the code is permitted to
copy the return of Func() into a temporary of type "const
vector<string>" before binding it to cvec (see 8.5.3/5), and it is
unspecified whether it will do so or not, so basically the code is not
necessarily absent of undefined behaviour.

Tom

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: johnchx2@yahoo.com
Date: Mon, 8 May 2006 22:08:51 CST
Raw View
Tom   s wrote:

> (I'll admit that I'm not certain if ALL temporaries are non-const throughout
> the entire language. Could someone please clarify that?)

It depends on the function type.  In this example, the return value of
g() is const, while the return value h() is non-const:

   struct foo { void f() {} };

   const foo g() { return foo(); }
   foo h() { return foo(); }

   int main ()
   {
      g().f();    // error: return value of g() is const
      h().f();
   }


> I have a question, which I have asked on comp.lang.c++, but which no-one has
> seemed willing to answer. Is the following translation unit well-formed, and
> absent of undefined behaviour?
>
> #include <string>
> using std::string;
>
> #include <vector>
> using std::vector;
>
> vector<string> Func()
> {
>     vector<string> vec;
>
>     vec.push_back("One");
>     vec.push_back("Two");
>
>     return vec;
> }
>
> #include <iostream>
> using std::cout; using std::endl;
>
> int main()
> {
>     const vector<string> &cvec = Func();
>
>     vector<string> &vec = const_cast< vector<string>& >(cvec);
>
>     vec.push_back("Three");
>
>     cout << vec.at(0) << endl
>          << vec.at(1) << endl
>          << vec.at(2) << endl;
> }

I don't see any problem with it.


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "SuperKoko" <tabkannaz@yahoo.fr>
Date: Tue, 9 May 2006 11:10:46 CST
Raw View
Tom   s wrote:

> I have a question, which I have asked on comp.lang.c++, but which no-one has
> seemed willing to answer. Is the following translation unit well-formed, and
> absent of undefined behaviour?
>
> #include <string>
> using std::string;
>
> #include <vector>
> using std::vector;
>
> vector<string> Func()
> {
>     vector<string> vec;
>
>     vec.push_back("One");
>     vec.push_back("Two");
>
>     return vec;
> }
>
> #include <iostream>
> using std::cout; using std::endl;
>
> int main()
> {
>     const vector<string> &cvec = Func();
>
>     vector<string> &vec = const_cast< vector<string>& >(cvec);
>
>     vec.push_back("Three");
>
>     cout << vec.at(0) << endl
>          << vec.at(1) << endl
>          << vec.at(2) << endl;
> }
>
Undefined behaviour!
See 8.5.3p8 [dcl.init.ref]
"--A temporary of type "cv1 T2" [sic] is created, and a constructor
        is  called  to copy the entire rvalue object into the
temporary.
        The reference is bound to  the  temporary  or  to  a
sub-object
        within the temporary.9)
"
Here cv1 is const... Thus the temporary is of type "const
std::vector<std::string>"
Modifying such temporary has UB.

It is clear that the type of the temporary is "cv1 T2", not "cv2 T2"


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: johnchx2@yahoo.com
Date: 9 May 2006 20:30:05 GMT
Raw View
Tom Widmer wrote:

> It may or may not be fine. The problem is that the code is permitted to
> copy the return of Func() into a temporary of type "const
> vector<string>" before binding it to cvec (see 8.5.3/5), and it is
> unspecified whether it will do so or not, so basically the code is not
> necessarily absent of undefined behaviour.

Good point.  But while that's technically strictly true, there are a
couple of reasons to doubt that it's practically significant.

First, the compiler's authority to make the copy is about to be revoked
by the standards committee.  See:

  http://www2.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#391

One of the things that makes this change possible is that nobody seems
to know of any real-world implementation which actually makes a copy in
this situation.  (Corrections welcome if someone knows of one.)

Second, even if a copy were made, so that the const cast would formally
result in undefined behavior, it is highly likely that it would work
anyway.  The principal reason for making casting away const-ness on a
"truly" const object undefined is to account for the possibility that
the const object might be placed in read-only memory of some sort.  In
the case of copying an rvalue, it's hard to see how the copy could wind
up in read only memory.  (It's theoretically possible, but the compiler
would have to work really hard to make it happen.)

One other possibility is that the optimizer might make the assumption
that the copied rvalue is immutable, so that changing its value could
result in unexpected results.  But introducing an unnecessary copy in
order to help the optimizer out seems a bit of a stretch.  I'd be
surprised if any real compiler did it in the sort of situation we're
discussing here.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Tom s" <NULL@NULL.NULL>
Date: Mon, 8 May 2006 10:37:38 CST
Raw View
It's perfectly legitimate to use "const_cast", and subsequently alter an
object, but only if the object was defined as non-const in the first place.
Example 1:

#include <string>

void Func( const std::string &cstr )
{
    std::string &str = const_cast<std::string&>(cstr);

    str += "monkey";
}

int main()
{
    std::string str("ape");

    Func(str);
}



I've seen many examples of where a temporary object is non-const. Example 2:


#include <string>
#include <vector>

std::vector<std::string> Func()
{
    std::vector<std::string> vec;

    vec.push_back("One");
    vec.push_back("Two");

    return vec;
}

int main()
{
    Func().push_back("Three"); // <-- Non-const member function
}


(I'll admit that I'm not certain if ALL temporaries are non-const throughout
the entire language. Could someone please clarify that?)


We can use a const reference to "extend the lifetime" of a temporary
returned-by-value from a function. Example 3:

#include <string>
#include <vector>

std::vector<std::string> Func()
{
    std::vector<std::string> vec;

    vec.push_back("One");
    vec.push_back("Two");

    return vec;
}

int main()
{
    const std::vector<std::string> &cvec = Func();

    std::string str( cvec.at(0) );

    str += "monkey";
}

We saw in Example 2, that the temporary object returned-by-value from
"Func" was non-const.

I have a question, which I have asked on comp.lang.c++, but which no-one has
seemed willing to answer. Is the following translation unit well-formed, and
absent of undefined behaviour?

#include <string>
using std::string;

#include <vector>
using std::vector;

vector<string> Func()
{
    vector<string> vec;

    vec.push_back("One");
    vec.push_back("Two");

    return vec;
}

#include <iostream>
using std::cout; using std::endl;

int main()
{
    const vector<string> &cvec = Func();

    vector<string> &vec = const_cast< vector<string>& >(cvec);

    vec.push_back("Three");

    cout << vec.at(0) << endl
         << vec.at(1) << endl
         << vec.at(2) << endl;
}



-Tom   s

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]