I've defined a custom view:
namespace detail {
constexpr auto coord = [](auto&& p) -> decltype(auto) {
return p.coord();
};
struct CoordsView
{
constexpr CoordsView() = default;
template<std::ranges::range R>
friend constexpr auto operator|(R&& r, CoordsView)
{
return std::forward<R>(r) | std::views::transform(coord);
}
};
} // namespace detail
inline constexpr detail::CoordsView coords;
and I am using it in the following way:
struct Point
{
float x, y, z;
};
class Vertex
{
Point p;
public:
Vertex(float x, float y, float z) : p{x, y, z} {}
Point& coord() { return p; }
const Point& coord() const { return p; }
};
int main()
{
std::vector<Vertex> v;
v.push_back(Vertex(-0.5, -0.5, 0.5));
v.push_back(Vertex(0.5, -0.5, 0.5));
v.push_back(Vertex(-0.5, 0.5, 0.5));
v.push_back(Vertex(0.5, 0.5, 0.5));
for (auto& p : v | coords) {
p.x += 1;
}
for (const auto& p : v | coords) {
std::cout << p.x << " " << p.y << " " << p.z << std::endl;
}
return 0;
}
Everything works fine on all the three major compilers when using old header and source files, or in a single main.cpp
file: https://godbolt.org/z/M7a4a5YrP
The problems start when using C++20 modules on Clang 18: https://godbolt.org/z/T1cbzY1Mn
views.ixx:
module;
#include <ranges>
export module myviews;
namespace detail {
constexpr auto coord = [](auto&& p) -> decltype(auto) {
return p.coord();
};
struct CoordsView
{
constexpr CoordsView() = default;
template<std::ranges::range R>
friend constexpr auto operator|(R&& r, CoordsView)
{
return std::forward<R>(r) | std::views::transform(coord);
}
};
} // namespace detail
export inline constexpr detail::CoordsView coords;
main.cpp:
#include <iostream>
#include <vector>
import myviews;
struct Point
{
float x, y, z;
};
class Vertex
{
Point p;
public:
Vertex(float x, float y, float z) : p{x, y, z} {}
Point& coord() { return p; }
const Point& coord() const { return p; }
};
int main()
{
std::vector<Vertex> v;
v.push_back(Vertex(-0.5, -0.5, 0.5));
v.push_back(Vertex(0.5, -0.5, 0.5));
v.push_back(Vertex(-0.5, 0.5, 0.5));
v.push_back(Vertex(0.5, 0.5, 0.5));
for (auto& p : v | coords) {
p.x += 1;
}
for (const auto& p : v | coords) {
std::cout << p.x << " " << p.y << " " << p.z << std::endl;
}
return 0;
}
I am getting the error:
views.ixx:20:35: error: invalid operands to binary expression ('std::vector<Vertex>' and '_Partial<_Transform, decay_t<const (lambda at /app/views.ixx:9:24) &>>' (aka '_Partial<std::ranges::views::_Transform, detail::(lambda at /app/views.ixx:9:24)>'))
20 | return std::forward<R>(r) | std::views::transform(coord);
| ~~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
which I don't understand.
On MSVC the code works, although I need to include #include <ranges>
also on the main file (should this really be necessary?).
What am I doing wrong? Is there a better way to implement this kind of views?
I got it to work by making coord
an inline
variable (reasoning was: it's at namespace scope, so it's static).
I'm not sure if it's well defined behaviour, though.