The following code (godbolt):
#include <iostream>
#include <vector>
#include <ranges>
struct S
{
float x = 150.f;
S f() const
{
return *this;
}
};
int main()
{
std::vector<S> vec{
{ 1.f },
{ 2.f }
};
std::cout << "\nCreated\n";
for ( const auto& l : vec )
{
std::cout << l.f().x << ' ';
}
std::cout << "\nView0\n";
for ( float t : vec
| std::views::transform( &S::f )
| std::views::transform( &S::x )
)
{
std::cout << t << ' ';
}
std::cout << "\nView1\n";
auto view1
= vec
| std::views::transform( &S::f )
| std::views::transform( [] ( const S& l ) { return l.x; } );
for ( float t : view1 )
{
std::cout << t << ' ';
}
}
produces the following output (for Clang and GCC with optimizations enabled):
Created
1 2
View0
0 0
View1
1 2
I've found that the zeros are not fixed, getting the output of View0
looks like undefined behavior, however I fail to see why.
Another observation is that enabling -Wall -Wextra -pedantic-errors
in GCC causes some warnings to appear:
<source>:33:52: warning: using a dangling pointer to an unnamed temporary [-Wdangling-pointer=]
33 | | std::views::transform( &S::x )
| ^
In file included from <source>:3:
/opt/compiler-explorer/gcc-14.1.0/include/c++/14.1.0/ranges:1949:54: note: unnamed temporary defined here
1949 | { return std::__invoke(*_M_parent->_M_fun, *_M_current); }
<source>:33:52: warning: '<unnamed>.S::x' may be used uninitialized [-Wmaybe-uninitialized]
33 | | std::views::transform( &S::x )
| ^
/opt/compiler-explorer/gcc-14.1.0/include/c++/14.1.0/ranges:1949:54: note: '<anonymous>' declared here
1949 | { return std::__invoke(*_M_parent->_M_fun, *_M_current); }
My question is: Why is the output after View0
not equal to the output after Created
and View1
?
-O3
there is no problem at all. – Welladvised&S::x
, when invoked, returns a reference. – Intermarry-fsanitize=address
. – Intermarrystd::invoke
ing&S::x
returns by reference, while your lambda returns by value. I'm not sure why the former is illegal, but this seems to be the difference. – Hawk[] ( const S& l ) { return l.x; }
returns a value, which is safe. Andstd::views::transform( &S::x )
uses a reference on a temporary, same as[] ( const S& l ) -> const float& { return l.x; }
, which is UB: gcc.godbolt.org/z/9951nT486 – Whall(float t:...)
and not(const float& t:...)
. – CassondracassouletS
destroyed before a reference on its field is accessed and copied infloat t :
– Whall__cpp_range_based_for==202211L
still have the issue, if that is what you're referring to. – Cassondracassoulet