I want to join multiple views, without using temporary variables (like arr_and_zeroes
in the example). However, doing so produces a compile error.
Is there any way to do it without using temporary variables?
I'm using g++ 14.2.1
#include <vector>
#include <ranges>
#include <type_traits>
int main() {
std::vector<int> arr{2, 3, 7, 5};
auto arr_and_zeroes = {{0}, arr, {0}};
static_assert(std::is_same_v<
decltype(arr_and_zeroes),
std::initializer_list<std::vector<int>>
>);
auto r1 = std::views::join(arr_and_zeroes);
// both of those assignments produce compile-time errors:
//auto r2 = std::views::join({{0}, arr, {0}});
//auto r3 = std::views::join(
// std::initializer_list<std::vector<int>>{{0}, arr, {0}}
//);
}
Error message on r2
assignment:
bug2.cpp: In function ‘int main()’:
bug2.cpp:15:29: error: no match for call to ‘(const std::ranges::views::_Join) (<brace-enclosed initializer list>)’
15 | auto r2 = std::views::join({{0}, arr, {0}});
| ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~
In file included from bug2.cpp:2:
/usr/include/c++/14.2.1/ranges:3227:9: note: candidate: ‘template<class _Range> requires (viewable_range<_Range>) && (__can_join_view<_Range>) constexpr auto
std::ranges::views::_Join::operator()(_Range&&) const’
3227 | operator() [[nodiscard]] (_Range&& __r) const
| ^~~~~~~~
/usr/include/c++/14.2.1/ranges:3227:9: note: template argument deduction/substitution failed:
bug2.cpp:15:29: note: couldn’t deduce template parameter ‘_Range’
15 | auto r2 = std::views::join({{0}, arr, {0}});
| ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~
Error message on r3
assignment:
bug2.cpp: In function ‘int main()’:
bug2.cpp:16:29: error: no match for call to ‘(const std::ranges::views::_Join) (std::initializer_list<std::vector<int> >)’
16 | auto r3 = std::views::join(
| ~~~~~~~~~~~~~~~~^
17 | std::initializer_list<std::vector<int>>{{0}, arr, {0}}
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
18 | );
| ~
In file included from bug2.cpp:2:
/usr/include/c++/14.2.1/ranges:3227:9: note: candidate: ‘template<class _Range> requires (viewable_range<_Range>) && (__can_join_view<_Range>) constexpr auto std::ranges::views::_Join::operator()(_Range&&) const’
3227 | operator() [[nodiscard]] (_Range&& __r) const
| ^~~~~~~~
/usr/include/c++/14.2.1/ranges:3227:9: note: template argument deduction/substitution failed:
/usr/include/c++/14.2.1/ranges:3227:9: note: constraints not satisfied
In file included from /usr/include/c++/14.2.1/bits/ranges_util.h:34,
from /usr/include/c++/14.2.1/tuple:44,
from /usr/include/c++/14.2.1/bits/uses_allocator_args.h:39,
from /usr/include/c++/14.2.1/bits/memory_resource.h:41,
from /usr/include/c++/14.2.1/vector:87,
from bug2.cpp:1:
bug2.cpp: In substitution of ‘template<class _Range> requires (viewable_range<_Range>) && (__can_join_view<_Range>) constexpr auto std::ranges::views::_Join::operator()(_Range&&) const [with _Range = std::initializer_list<std::vector<int> >]’:
bug2.cpp:16:29: required from here
16 | auto r3 = std::views::join(
| ~~~~~~~~~~~~~~~~^
17 | std::initializer_list<std::vector<int>>{{0}, arr, {0}}
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
18 | );
| ~
/usr/include/c++/14.2.1/bits/ranges_base.h:808:13: required for the satisfaction of ‘viewable_range<_Range>’ [with _Range = std::initializer_list<std::vector<int, std::allocator<int> > >]
/usr/include/c++/14.2.1/bits/ranges_base.h:810:11: note: no operand of the disjunction is satisfied
809 | && ((view<remove_cvref_t<_Tp>> && constructible_from<remove_cvref_t<_Tp>, _Tp>)
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
810 | || (!view<remove_cvref_t<_Tp>>
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
811 | && (is_lvalue_reference_v<_Tp>
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
812 | || (movable<remove_reference_t<_Tp>>
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
813 | && !__detail::__is_initializer_list<remove_cvref_t<_Tp>>))));
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1plus: note: set ‘-fconcepts-diagnostics-depth=’ to at least 2 for more detail
It seems like join must be passed a viewable_range
, with its concept outlined here:
template< class T >
concept viewable_range =
ranges::range<T> &&
((ranges::view<std::remove_cvref_t<T>> &&
std::constructible_from<std::remove_cvref_t<T>, T>) ||
(!ranges::view<std::remove_cvref_t<T>> &&
(std::is_lvalue_reference_v<T> ||
(std::movable<std::remove_reference_t<T>> && !/*is-initializer-list*/<T>))));
Referencing the last line, initializer lists are explicitly disallowed unless std::remove_cvref_t<T>
is a specialization of std::initializer_list
.
You can see cppreference to see an example of viable viewable_ranges
that would work.
If you are using C++26 you can use std::ranges::views::concat
instead:
int main() {
using namespace std::views;
std::vector<int> arr{2, 3, 7, 5};
auto cat = concat(single(0), arr, single(0));
}
If you aren't using C++26, creating a temporary and using join is probably the simplest solution without implementing concat yourself.