I'm trying out this demo code to play with std::ranges::views::cartesian_product
in g++13. I'm wondering about the for
loop in main
:
#include <cstdio>
#include <tuple>
#include <array>
#include <iostream>
#include <ranges>
typedef std::tuple<int const&, int const&, int const&> int_triple;
void print(int_triple x){
std::cout << std::get<0>(x) << " " <<std::get<1>(x) << " "<<std::get<2>(x) << " " << std::endl;
}
int main(void){
std::array<int, 2> x = {1,2};
std::array<int, 2> y = {3,4};
std::array<int, 2> z = {5, 6};
for(const int_triple& t: std::views::cartesian_product(x,y,z)){
print(t);
}
return 0;
}
Why is this OK:
for(const int_triple& t: std::views::cartesian_product(x,y,z))
but this throws the following error:
for(int_triple& t: std::views::cartesian_product(x,y,z))
cartprod_demo.cpp:16:59: error: cannot bind non-const lvalue reference of type ‘int_triple&’ {aka ‘std::tuple<const int&, const int&, const int&>&’} to an rvalue of type ‘int_triple’ {aka ‘std::tuple<const int&, const int&, const int&>’}
16 | for(int_triple& t: std::views::cartesian_product(x,y,z)){
| ^
In file included from cartprod_demo.cpp:2:
/usr/include/c++/13/tuple:930:9: note: after user-defined conversion: ‘constexpr std::tuple< <template-parameter-1-1> >::tuple(std::tuple<_Args1 ...>&&) [with _UElements = {int&, int&, int&}; bool _Valid = true; typename std::enable_if<_TCC<_Valid>::__is_implicitly_constructible<_UElements ...>(), bool>::type <anonymous> = true; _Elements = {const int&, const int&, const int&}]’
How do I read this error?
I'm new to C++, so I want to understand this terse syntax. I get that the lvalue
is just the variable t
on the left-hand side, but that's pretty much all I can get from this.
cartesian_product()
returns a view, and then the for
loop uses that view's iterator
s to access the view's elements. The for
loop dereferences those iterators to access the individual elements of the view and assign them to your t
variable.
Your code:
for(const int_triple& t: std::views::cartesian_product(x,y,z)){
print(t);
}
Is effectively equivalent to this:
{
auto&& range = std::views::cartesian_product(x,y,z);
auto begin = range.begin();
auto end = range.end();
for ( ; begin != end; ++begin)
{
const int_triple& t = *begin; // <-- problem here
print(t);
}
}
In this situation, the iterator
dereference is returning each tuple
element by value, thus they are rvalues.
A const reference can bind to an rvalue, which is why const int_triple& t
works, but a non-const reference cannot, which is why int_triple& t
fails. And that is exactly what the error message is complaining about:
cannot bind non-const lvalue reference of type ‘int_triple&’ ... to an rvalue of type ‘int_triple’ ...