Такой оператор компилируется без ошибок:
for (int i : std::vector<int>({0, 1}))
std::cout << i;
Но такое заявление не делает:
std::vector<int>({0, 1}) |
std::views::filter([](int i) { return true; });
Почему r-значения разрешены для каждого цикла, но не для каналов std::range? Есть ли способ заставить что-то вроде второго работать без объявления переменной?
И лямбда должна возвращать логическое значение, чтобы быть допустимым предикатом.
Я думаю, что std::views::filter
пытается защитить вас от создания представления, основанного на уничтоженном временном
Это разрешено в C++23.
@康桓瑋 Можете уточнить, что изменилось?
Цикл for работает нормально, потому что vector<int>
(rvalue) действителен, пока цикл оценивается.
Второй фрагмент кода имеет 2 проблемы:
bool
vector<int>
, который вы используете в объекте представлений, к моменту его оценки болтается, поэтому компилятор его не принимает.Если вместо vector<int>
rvalue объект не может болтаться (lvalue) или содержит ссылки на что-то еще, что не может не болтаться, это сработает.
Например:
auto vec = std::vector<int>({0, 1});
auto vw = vec | std::ranges::views::filter([](int i) { std::cout << i; return true; });
Или вы можете передать rvalue со ссылками на независающий объект vector<int>
, например std::span
:
auto vec = std::vector<int>({0, 1});
auto vw = span{vec} | std::ranges::views::filter([](int i) { std::cout << i; return true; })
Здесь std::span
все еще является rvalue, но компилятор принимает его, потому что автор std::span
создал для него исключение.
Для пользовательского типа со ссылками на что-то другое (обычно представления) вы можете создать собственное исключение, создав специализацию переменной шаблона enable_borrowed_range
.
В случае вектора это будет выглядеть так (но не делай этого!):
template<> // don't do this
inline constexpr bool ranges::enable_borrowed_range<std::vector<int>> = true;
Теперь ваш код компилируется, но он вызовет неопределенное поведение, потому что вектор зависает после оценки представления.
Это работает с последними версиями MSVC STL и libstdc++ (демо) благодаря P2415R2.
До P2415R2 filter_view
всегда сохраняет базовый vector
по ссылке (через ref_view
). Было сочтено неразумным хранить копию vector
. Потому что представление должно быть дешевым для копирования, дешевого перемещения и дешевого уничтожения. Хранение копии vector
приведет к неожиданному снижению производительности.
И из-за проблем со сроком службы не имеет смысла иметь ссылку на rvalue vector
.
Но P2415R2 указывает, что вид также может быть не копируемым, но при этом его можно дешево перемещать и достаточно дешево уничтожать.
Поэтому P2415R2 представил owning_view
, некопируемое представление, в котором хранится копия диапазона rvalue, и заставил стандартные адаптеры диапазона использовать это представление.
Конечным результатом является то, что создание filter_view
из vector
rvalue работает с реализациями стандартной библиотеки, которые реализуют P2415R2.
Я использую GCC 11.1.0. Я так понимаю это будет в новой версии? Можете ли вы придумать какие-либо обходные пути? Лучшее, что у меня есть, это использовать синглтон, но мы многопоточные.
Пожалуйста, опубликуйте сообщение об ошибке, которое создает второй случай.