Я определил собственное представление:
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;
и я использую его следующим образом:
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;
}
Все отлично работает на всех трех основных компиляторах при использовании старых заголовочных и исходных файлов или в одном main.cpp
файле: https://godbolt.org/z/M7a4a5YrP
Проблемы начинаются при использовании модулей C++20 в Clang 18: https://godbolt.org/z/T1cbzY1Mn
просмотры.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;
основной.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;
}
Я получаю сообщение об ошибке:
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);
| ~~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
чего я не понимаю.
На MSVC код работает, хотя мне нужно включить #include <ranges>
и в основной файл (должно ли это быть обязательно?).
Что я делаю не так? Есть ли лучший способ реализовать такие представления?
Я заставил его работать, создав coord
переменную inline
(причина заключалась в том, что она находится в области пространства имен, поэтому она статична).
Однако я не уверен, что это четко определенное поведение.
Прямая трансляция в Compiler Explorer
Ух ты, это было легко! Это работает, спасибо!