Don't understand this problem with ranges::v3 - c++

I have the following code fragment using ranges::v3:
#include <range/v3/view/all.hpp>
#include <range/v3/view/filter.hpp>
#include <range/v3/view/join.hpp>
#include <range/v3/range/conversion.hpp>
#include <unordered_map>
#include <limits>
#include <stdint.h>
#include <sys/socket.h>
#include <string>
struct EncodedValueDecoder: std::unordered_map<int, uint8_t> {
using Base_t = std::unordered_map<int, uint8_t>;
EncodedValueDecoder(int mask, const Base_t& values) :
Base_t(values), mask_(mask == 0 ? std::numeric_limits<int>::max() : mask) {
}
std::string findAll(const int target) const {
return *this | ranges::views::filter([&target, this](const typename Base_t::value_type& item) {
return bitSet(item.first, target);
}) | ranges::views::join(",") | ranges::to<std::string>();
}
int maskedValue(const int target) const {
return static_cast<int>(target & mask_);
}
bool bitSet(const int bit, const int target) const {
return bit & target & mask_;
}
int mask_;
};
I get the following errors
g++ -std=c++23 -O0 -g3 -pedantic -pedantic-errors -Wall -Wextra -Werror -Wconversion -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wlogical-op -Wmissing-declarations -Wnoexcept -Wold-style-cast -Woverloaded-virtual -Wshadow -Wsign-conversion -Wsign-promo -Wswitch-default -Wfloat-equal -c -pthread -Winvalid-pch -fmessage-length=0 -Wdouble-promotion -Wimplicit-fallthrough=5 -Wuninitialized -Winit-self -Wsuggest-override -Wduplicated-branches -Wduplicated-cond -Wtrampolines -Wcast-qual -Wuseless-cast -Wsized-deallocation -ftemplate-backtrace-limit=0 -fPIC -o src/main.o ../src/main.cpp
../src/main.cpp: In member function ‘std::string EncodedValueDecoder::findAll(int) const’:
../src/main.cpp:20:20: error: no match for ‘operator|’ (operand types are ‘ranges::filter_view<ranges::ref_view<const EncodedValueDecoder>, EncodedValueDecoder::findAll(int) const::<lambda(const std::unordered_map<int, unsigned char>::value_type&)> >’ and ‘ranges::views::view_closure<ranges::detail::bind_back_fn_<ranges::views::join_base_fn, ranges::detail::reference_wrapper_<const char [2]> > >’)
18 | return *this | ranges::views::filter([&target, this](const typename Base_t::value_type& item) {
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| |
| ranges::filter_view<ranges::ref_view<const EncodedValueDecoder>, EncodedValueDecoder::findAll(int) const::<lambda(const std::unordered_map<int, unsigned char>::value_type&)> >
19 | return bitSet(item.first, target);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20 | }) | ranges::views::join(",") | ranges::to<std::string>();
| ~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~
| |
| ranges::views::view_closure<ranges::detail::bind_back_fn_<ranges::views::join_base_fn, ranges::detail::reference_wrapper_<const char [2]> > >
In file included from /usr/include/range/v3/view/all.hpp:28,
from ../src/main.cpp:1:
/usr/include/range/v3/view/view.hpp:142:35: note: candidate: ‘template<class ViewFn, class Pipeable> constexpr concepts::return_t<ranges::views::view_closure<ranges::composed<Pipeable, ViewFn> >, typename std::enable_if<(is_pipeable_v<Pipeable> && concepts::detail::CPP_true_fn(concepts::detail::Nil{})), void>::type> ranges::views::view_closure_base_ns::operator|(ranges::views::view_closure<Fun>, Pipeable)’
142 | friend constexpr auto operator|(view_closure<ViewFn> vw, Pipeable pipe)
| ^~~~~~~~
/usr/include/range/v3/view/view.hpp:142:35: note: template argument deduction/substitution failed:
../src/main.cpp:20:45: note: ‘ranges::filter_view<ranges::ref_view<const EncodedValueDecoder>, EncodedValueDecoder::findAll(int) const::<lambda(const std::unordered_map<int, unsigned char>::value_type&)> >’ is not derived from ‘ranges::views::view_closure<Fun>’
20 | }) | ranges::views::join(",") | ranges::to<std::string>();
| ^
/usr/include/range/v3/view/view.hpp:127:13: note: candidate: ‘template<class Rng, class ViewFn> constexpr concepts::return_t<Rng, typename std::enable_if<((range<Rng> && (! viewable_range<Rng>)) && concepts::detail::CPP_true_fn(concepts::detail::Nil{})), void>::type> ranges::views::view_closure_base_ns::operator|(Rng&&, const ranges::views::view_closure<ViewFn>&)’ (deleted)
127 | operator|(Rng &&, view_closure<ViewFn> const &) // ******* READ THIS ********
| ^~~~~~~~
/usr/include/range/v3/view/view.hpp:127:13: note: template argument deduction/substitution failed:
/usr/include/range/v3/view/view.hpp: In substitution of ‘template<class Rng, class ViewFn> constexpr concepts::return_t<Rng, typename std::enable_if<((range<Rng> && (! viewable_range<Rng>)) && concepts::detail::CPP_true_fn(concepts::detail::Nil{})), void>::type> ranges::views::view_closure_base_ns::operator|(Rng&&, const ranges::views::view_closure<ViewFn>&) [with Rng = ranges::filter_view<ranges::ref_view<const EncodedValueDecoder>, EncodedValueDecoder::findAll(int) const::<lambda(const std::unordered_map<int, unsigned char>::value_type&)> >; ViewFn = ranges::detail::bind_back_fn_<ranges::views::join_base_fn, ranges::detail::reference_wrapper_<const char [2]> >]’:
../src/main.cpp:20:31: required from here
/usr/include/range/v3/view/view.hpp:127:13: error: no type named ‘type’ in ‘struct std::enable_if<false, void>’
/usr/include/range/v3/view/view.hpp:117:35: note: candidate: ‘template<class Rng, class ViewFn> requires (viewable_range<Rng>) && (invocable_view_closure<ViewFn, Rng>) constexpr auto ranges::views::view_closure_base_ns::operator|(Rng&&, ranges::views::view_closure<ViewFn>)’
117 | friend constexpr auto operator|(Rng && rng, view_closure<ViewFn> vw)
| ^~~~~~~~
/usr/include/range/v3/view/view.hpp:117:35: note: template argument deduction/substitution failed:
/usr/include/range/v3/view/view.hpp:117:35: note: constraints not satisfied
In file included from /usr/include/range/v3/range_fwd.hpp:22,
from /usr/include/range/v3/view/all.hpp:20:
/usr/include/range/v3/functional/concepts.hpp: In substitution of ‘template<class Rng, class ViewFn> requires (viewable_range<Rng>) && (invocable_view_closure<ViewFn, Rng>) constexpr auto ranges::views::view_closure_base_ns::operator|(Rng&&, ranges::views::view_closure<ViewFn>) [with Rng = ranges::filter_view<ranges::ref_view<const EncodedValueDecoder>, EncodedValueDecoder::findAll(int) const::<lambda(const std::unordered_map<int, unsigned char>::value_type&)> >; ViewFn = ranges::detail::bind_back_fn_<ranges::views::join_base_fn, ranges::detail::reference_wrapper_<const char [2]> >]’:
../src/main.cpp:20:31: required from here
/usr/include/range/v3/functional/concepts.hpp:40:5: required for the satisfaction of ‘invocable_requires_<Fun, Args ...>’ [with Fun = ranges::detail::bind_back_fn_<ranges::views::join_base_fn, ranges::detail::reference_wrapper_<const char[2]> >; Args = {ranges::filter_view<ranges::ref_view<const EncodedValueDecoder>, EncodedValueDecoder::findAll::._anon_127>}]
/usr/include/range/v3/functional/concepts.hpp:48:17: required for the satisfaction of ‘invocable<ViewFn, Rng>’ [with ViewFn = ranges::detail::bind_back_fn_<ranges::views::join_base_fn, ranges::detail::reference_wrapper_<const char[2]> >; Rng = ranges::filter_view<ranges::ref_view<const EncodedValueDecoder>, EncodedValueDecoder::findAll::._anon_127>]
/usr/include/range/v3/view/view.hpp:83:17: required for the satisfaction of ‘invocable_view_closure<ViewFn, Rng>’ [with ViewFn = ranges::detail::bind_back_fn_<ranges::views::join_base_fn, ranges::detail::reference_wrapper_<const char[2]> >; Rng = ranges::filter_view<ranges::ref_view<const EncodedValueDecoder>, EncodedValueDecoder::findAll::._anon_127>]
/usr/include/range/v3/functional/concepts.hpp:40:5: in requirements with ‘Fun&& fn’ [with Args = {ranges::filter_view<ranges::ref_view<const EncodedValueDecoder>, EncodedValueDecoder::findAll::._anon_127>}; Fun = ranges::detail::bind_back_fn_<ranges::views::join_base_fn, ranges::detail::reference_wrapper_<const char[2]> >]
/usr/include/range/v3/functional/concepts.hpp:40:5: note: the required expression ‘ranges::invoke((Fun&&)(fn), (declval<Args>)()...)’ is invalid
40 | CPP_requires(invocable_,
| ^~~~~~~~~~~~
cc1plus: note: set ‘-fconcepts-diagnostics-depth=’ to at least 2 for more detail
In file included from /usr/include/meta/meta.hpp:18,
from /usr/include/range/v3/view/all.hpp:18:
/usr/include/c++/12.1.0/cstddef:132:3: note: candidate: ‘constexpr std::byte std::operator|(byte, byte)’
132 | operator|(byte __l, byte __r) noexcept
| ^~~~~~~~
/usr/include/c++/12.1.0/cstddef:132:18: note: no known conversion for argument 1 from ‘ranges::filter_view<ranges::ref_view<const EncodedValueDecoder>, EncodedValueDecoder::findAll(int) const::<lambda(const std::unordered_map<int, unsigned char>::value_type&)> >’ to ‘std::byte’
132 | operator|(byte __l, byte __r) noexcept
| ~~~~~^~~
In file included from /usr/include/c++/12.1.0/streambuf:41,
from /usr/include/c++/12.1.0/bits/streambuf_iterator.h:35,
from /usr/include/c++/12.1.0/iterator:66,
from /usr/include/range/v3/range/access.hpp:21,
from /usr/include/range/v3/view/all.hpp:22:
/usr/include/c++/12.1.0/bits/ios_base.h:87:3: note: candidate: ‘constexpr std::_Ios_Fmtflags std::operator|(_Ios_Fmtflags, _Ios_Fmtflags)’
87 | operator|(_Ios_Fmtflags __a, _Ios_Fmtflags __b)
| ^~~~~~~~
/usr/include/c++/12.1.0/bits/ios_base.h:87:27: note: no known conversion for argument 1 from ‘ranges::filter_view<ranges::ref_view<const EncodedValueDecoder>, EncodedValueDecoder::findAll(int) const::<lambda(const std::unordered_map<int, unsigned char>::value_type&)> >’ to ‘std::_Ios_Fmtflags’
87 | operator|(_Ios_Fmtflags __a, _Ios_Fmtflags __b)
| ~~~~~~~~~~~~~~^~~
/usr/include/c++/12.1.0/bits/ios_base.h:130:3: note: candidate: ‘constexpr std::_Ios_Openmode std::operator|(_Ios_Openmode, _Ios_Openmode)’
130 | operator|(_Ios_Openmode __a, _Ios_Openmode __b)
| ^~~~~~~~
/usr/include/c++/12.1.0/bits/ios_base.h:130:27: note: no known conversion for argument 1 from ‘ranges::filter_view<ranges::ref_view<const EncodedValueDecoder>, EncodedValueDecoder::findAll(int) const::<lambda(const std::unordered_map<int, unsigned char>::value_type&)> >’ to ‘std::_Ios_Openmode’
130 | operator|(_Ios_Openmode __a, _Ios_Openmode __b)
| ~~~~~~~~~~~~~~^~~
/usr/include/c++/12.1.0/bits/ios_base.h:170:3: note: candidate: ‘constexpr std::_Ios_Iostate std::operator|(_Ios_Iostate, _Ios_Iostate)’
170 | operator|(_Ios_Iostate __a, _Ios_Iostate __b)
| ^~~~~~~~
/usr/include/c++/12.1.0/bits/ios_base.h:170:26: note: no known conversion for argument 1 from ‘ranges::filter_view<ranges::ref_view<const EncodedValueDecoder>, EncodedValueDecoder::findAll(int) const::<lambda(const std::unordered_map<int, unsigned char>::value_type&)> >’ to ‘std::_Ios_Iostate’
170 | operator|(_Ios_Iostate __a, _Ios_Iostate __b)
| ~~~~~~~~~~~~~^~~
In file included from /usr/include/c++/12.1.0/bits/shared_ptr_atomic.h:33,
from /usr/include/c++/12.1.0/memory:78,
from /usr/include/range/v3/utility/addressof.hpp:15,
from /usr/include/range/v3/iterator/basic_iterator.hpp:29,
from /usr/include/range/v3/iterator/reverse_iterator.hpp:20,
from /usr/include/range/v3/range/access.hpp:37:
/usr/include/c++/12.1.0/bits/atomic_base.h:98:3: note: candidate: ‘constexpr std::memory_order std::operator|(memory_order, __memory_order_modifier)’
98 | operator|(memory_order __m, __memory_order_modifier __mod)
| ^~~~~~~~
/usr/include/c++/12.1.0/bits/atomic_base.h:98:26: note: no known conversion for argument 1 from ‘ranges::filter_view<ranges::ref_view<const EncodedValueDecoder>, EncodedValueDecoder::findAll(int) const::<lambda(const std::unordered_map<int, unsigned char>::value_type&)> >’ to ‘std::memory_order’
98 | operator|(memory_order __m, __memory_order_modifier __mod)
| ~~~~~~~~~~~~~^~~
../src/main.cpp: In member function ‘int EncodedValueDecoder::maskedValue(int) const’:
../src/main.cpp:23:24: error: useless cast to type ‘int’ [-Werror=useless-cast]
23 | return static_cast<int>(target & mask_);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1plus: all warnings being treated as errors
It appears to be complaining about the absence of a suitable overload for operator|. Given that I adapted this code from some examples I've seen in blogs dealing with ranges::v3, I've obviously misunderstood something. Can anyone point out my error?

The code I've posted is nonsense which is why it wouldn't compile. Kudos to JaMiT. If I could find a way to award a gold star, I would.

Related

How to use a C++20 istream_view?

I cannot find a single example on the web or in C++20 books that shows how the results of a std::ranges::istream_view can be used with follow-up operations. Every follow-up operation that I try to use results in compiler errors that are too difficult for me to figure out alone.
For example, how should I split a lazy stream of characters that comes in through an istream_view? The following code does not work, but hopefully communicates what I am trying to achieve:
#include <cstdlib>
#include <ranges>
#include <sstream>
#include <fmt/core.h>
auto main() -> int
{
std::istringstream data{"a,b"};
for (const auto& item:
std::views::istream<std::string>(data)
| std::views::split(",")
) {
fmt::print("{}\n", item);
}
return EXIT_SUCCESS;
}
^ I expect this to write "a" on the first line, and "b" on the second line.
GCC 12.2 emits an intimidating error message:
Could not execute the program
Compiler returned: 1
Compiler stderr
<source>: In function 'int main()':
<source>:11:5: error: no match for 'operator|' (operand types are 'std::ranges::basic_istream_view<std::__cxx11::basic_string<char>, char, std::char_traits<char> >' and 'std::ranges::views::__adaptor::_Partial<std::ranges::views::_Split, const char*>')
10 | std::views::istream<std::string>(data)
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| |
| std::ranges::basic_istream_view<std::__cxx11::basic_string<char>, char, std::char_traits<char> >
11 | | std::views::split(",")
| ^ ~~~~~~~~~~~~~~~~~~~~~~
| |
| std::ranges::views::__adaptor::_Partial<std::ranges::views::_Split, const char*>
In file included from <source>:2:
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/ranges:868:7: note: candidate: 'template<class _Lhs, class _Rhs> requires (derived_from<_Lhs, std::ranges::views::__adaptor::_RangeAdaptorClosure>) && (derived_from<_Rhs, std::ranges::views::__adaptor::_RangeAdaptorClosure>) constexpr auto std::ranges::views::__adaptor::operator|(_Lhs, _Rhs)'
868 | operator|(_Lhs __lhs, _Rhs __rhs)
| ^~~~~~~~
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/ranges:868:7: note: template argument deduction/substitution failed:
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/ranges:868:7: note: constraints not satisfied
In file included from /opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/ranges:37:
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/concepts: In substitution of 'template<class _Lhs, class _Rhs> requires (derived_from<_Lhs, std::ranges::views::__adaptor::_RangeAdaptorClosure>) && (derived_from<_Rhs, std::ranges::views::__adaptor::_RangeAdaptorClosure>) constexpr auto std::ranges::views::__adaptor::operator|(_Lhs, _Rhs) [with _Lhs = std::ranges::basic_istream_view<std::__cxx11::basic_string<char>, char, std::char_traits<char> >; _Rhs = std::ranges::views::__adaptor::_Partial<std::ranges::views::_Split, const char*>]':
<source>:11:28: required from here
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/concepts:67:13: required for the satisfaction of 'derived_from<_Lhs, std::ranges::views::__adaptor::_RangeAdaptorClosure>' [with _Lhs = std::ranges::basic_istream_view<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, char, std::char_traits<char> >]
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/concepts:67:28: note: 'std::ranges::views::__adaptor::_RangeAdaptorClosure' is not a base of 'std::ranges::basic_istream_view<std::__cxx11::basic_string<char>, char, std::char_traits<char> >'
67 | concept derived_from = __is_base_of(_Base, _Derived)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/ranges:859:7: note: candidate: 'template<class _Self, class _Range> requires (derived_from<typename std::remove_cvref<_Tp>::type, std::ranges::views::__adaptor::_RangeAdaptorClosure>) && (__adaptor_invocable<_Self, _Range>) constexpr auto std::ranges::views::__adaptor::operator|(_Range&&, _Self&&)'
859 | operator|(_Range&& __r, _Self&& __self)
| ^~~~~~~~
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/ranges:859:7: note: template argument deduction/substitution failed:
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/ranges:859:7: note: constraints not satisfied
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/ranges: In substitution of 'template<class _Self, class _Range> requires (derived_from<typename std::remove_cvref<_Tp>::type, std::ranges::views::__adaptor::_RangeAdaptorClosure>) && (__adaptor_invocable<_Self, _Range>) constexpr auto std::ranges::views::__adaptor::operator|(_Range&&, _Self&&) [with _Self = std::ranges::views::__adaptor::_Partial<std::ranges::views::_Split, const char*>; _Range = std::ranges::basic_istream_view<std::__cxx11::basic_string<char>, char, std::char_traits<char> >]':
<source>:11:28: required from here
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/ranges:831:13: required for the satisfaction of '__adaptor_invocable<_Self, _Range>' [with _Self = std::ranges::views::__adaptor::_Partial<std::ranges::views::_Split, const char*>; _Range = std::ranges::basic_istream_view<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, char, std::char_traits<char> >]
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/ranges:832:9: in requirements [with _Args = {std::ranges::basic_istream_view<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, char, std::char_traits<char> >}; _Adaptor = std::ranges::views::__adaptor::_Partial<std::ranges::views::_Split, const char*>]
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/ranges:832:44: note: the required expression 'declval<_Adaptor>()((declval<_Args>)()...)' is invalid
832 | = requires { std::declval<_Adaptor>()(declval<_Args>()...); };
| ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~
cc1plus: note: set '-fconcepts-diagnostics-depth=' to at least 2 for more detail
In file included from /opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/streambuf:41,
from /opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/streambuf_iterator.h:35,
from /opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/iterator:66,
from /opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/ranges:43:
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/ios_base.h:87:3: note: candidate: 'constexpr std::_Ios_Fmtflags std::operator|(_Ios_Fmtflags, _Ios_Fmtflags)'
87 | operator|(_Ios_Fmtflags __a, _Ios_Fmtflags __b)
| ^~~~~~~~
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/ios_base.h:87:27: note: no known conversion for argument 1 from 'std::ranges::basic_istream_view<std::__cxx11::basic_string<char>, char, std::char_traits<char> >' to 'std::_Ios_Fmtflags'
87 | operator|(_Ios_Fmtflags __a, _Ios_Fmtflags __b)
| ~~~~~~~~~~~~~~^~~
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/ios_base.h:130:3: note: candidate: 'constexpr std::_Ios_Openmode std::operator|(_Ios_Openmode, _Ios_Openmode)'
130 | operator|(_Ios_Openmode __a, _Ios_Openmode __b)
| ^~~~~~~~
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/ios_base.h:130:27: note: no known conversion for argument 1 from 'std::ranges::basic_istream_view<std::__cxx11::basic_string<char>, char, std::char_traits<char> >' to 'std::_Ios_Openmode'
130 | operator|(_Ios_Openmode __a, _Ios_Openmode __b)
| ~~~~~~~~~~~~~~^~~
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/ios_base.h:170:3: note: candidate: 'constexpr std::_Ios_Iostate std::operator|(_Ios_Iostate, _Ios_Iostate)'
170 | operator|(_Ios_Iostate __a, _Ios_Iostate __b)
| ^~~~~~~~
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/ios_base.h:170:26: note: no known conversion for argument 1 from 'std::ranges::basic_istream_view<std::__cxx11::basic_string<char>, char, std::char_traits<char> >' to 'std::_Ios_Iostate'
170 | operator|(_Ios_Iostate __a, _Ios_Iostate __b)
| ~~~~~~~~~~~~~^~~
In file included from /opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/span:42,
from /opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/ranges:45:
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/cstddef:132:3: note: candidate: 'constexpr std::byte std::operator|(byte, byte)'
132 | operator|(byte __l, byte __r) noexcept
| ^~~~~~~~
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/cstddef:132:18: note: no known conversion for argument 1 from 'std::ranges::basic_istream_view<std::__cxx11::basic_string<char>, char, std::char_traits<char> >' to 'std::byte'
132 | operator|(byte __l, byte __r) noexcept
| ~~~~~^~~
views::istream is an input range. So any adaptor that you pipe that into has to work on input ranges. views::split requires forward-or-better, which is why you can't split an istream.
But there is also a views::lazy_split that provides a slight differently interface such that it does work for input ranges (see P2210 for added context for why we wanted the forward+ split adaptor).
However, then you have the issue that views::istream<string> is a range of string that you're trying split on a comma. But that's not quite right, what you want you want is a range of char so that you end up with a range of range of char. What you mean is views::istream<char>.
Fixing that, this works:
auto main() -> int
{
std::istringstream data{"a,b"};
fmt::print("{}\n",
std::views::istream<char>(data)
| std::views::lazy_split(','));
}
That prints [['a'], ['b']]

error: no matching function for call to ‘std::tuple<synchronizer>::tuple(<brace-enclosed initializer list>)’

I'm getting a really big, scary, puzzling error in some code I've been working on lately. Basically, I'm trying to declare a thread and put it inside a std::vector for safe keeping. However, for some reason this gets std::tuple involved, which doesn't like what I'm doing.
How do I fix this?
P.S. I've already looked at this. If the answer's there, I'm not seeing it.
synchronizer.cpp:
#include <sys/socket.h>
#include <vector>
#include <thread>
#include <iostream>
#define PACKET_SIZE 8192
class synchronizer
{
private:
int sock;
int* status;
public:
int start();
synchronizer(int socket, int* stat);
~synchronizer();
void operator()();
};
synchronizer::synchronizer(int socket, int* stat) : sock(socket), status(stat) {}
void synchronizer::operator()()
{
this->start();
}
int synchronizer::start()
{
std::cout << "Starting." << std::endl;
char buf[PACKET_SIZE];
if (recv(sock, buf, PACKET_SIZE, 0) == -1)
{
perror("Error receiving");
return -1;
}
return 0;
}
synchronizer::~synchronizer()
{
}
The relevant bits of server.cpp, which is the main file:
#include <iostream>
#include <thread>
#include "synchronizer.hpp"
inline int checkin (std::vector<std::thread> *pool, int conn, int *status)
{
synchronizer syncer = synchronizer(conn, status);
pool->push_back(std::thread(syncer));
return 0;
}
int main(void)
{
std::vector<std::thread> pool = {};
int status = 0;
// newfd being a socket file descriptor acquired earlier in the program's logic
checkin(&pool, newfd, &status);
}
And here's the error (compiling with option -pthread):
In file included from server.cpp:14:
/usr/include/c++/9/thread: In instantiation of ‘static std::thread::_Invoker<std::tuple<typename std::decay<_Tp>::type, typename std::decay<_Args>::type ...> > std::thread::__make_invoker(_Callable&&, _Args&& ...) [with _Callable = synchronizer&; _Args = {}; typename std::decay<_Tp>::type = synchronizer]’:
/usr/include/c++/9/thread:131:22: required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = synchronizer&; _Args = {}; <template-parameter-1-3> = void]’
server.cpp:44:49: required from here
/usr/include/c++/9/thread:267:4: error: no matching function for call to ‘std::tuple<synchronizer>::tuple(<brace-enclosed initializer list>)’
267 | } };
| ^
In file included from /usr/include/c++/9/bits/unique_ptr.h:37,
from /usr/include/c++/9/memory:80,
from /usr/include/c++/9/thread:39,
from server.cpp:14:
/usr/include/c++/9/tuple:808:11: note: candidate: ‘template<class _Alloc, class _Dummy, class ... _UElements, typename std::enable_if<((std::tuple<synchronizer>::_TMCT<_UElements ...>::_MoveConstructibleTuple<_UElements ...>() && (! std::tuple<synchronizer>::_TMCT<_UElements ...>::_ImplicitlyMoveConvertibleTuple<_UElements ...>())) && std::tuple<synchronizer>::_TNTC<_Dummy>::_NonNestedTuple<tuple<_Tail ...>&&>()), bool>::type <anonymous> > std::tuple<_Elements>::tuple(std::allocator_arg_t, const _Alloc&, std::tuple<_Args2 ...>&&)’
808 | explicit tuple(allocator_arg_t __tag, const _Alloc& __a,
| ^~~~~
/usr/include/c++/9/tuple:808:11: note: template argument deduction/substitution failed:
In file included from server.cpp:14:
/usr/include/c++/9/thread:267:4: note: candidate expects 3 arguments, 1 provided
267 | } };
| ^
In file included from /usr/include/c++/9/bits/unique_ptr.h:37,
from /usr/include/c++/9/memory:80,
from /usr/include/c++/9/thread:39,
from server.cpp:14:
/usr/include/c++/9/tuple:793:2: note: candidate: ‘template<class _Alloc, class _Dummy, class ... _UElements, typename std::enable_if<((std::tuple<synchronizer>::_TMCT<_UElements ...>::_MoveConstructibleTuple<_UElements ...>() && std::tuple<synchronizer>::_TMCT<_UElements ...>::_ImplicitlyMoveConvertibleTuple<_UElements ...>()) && std::tuple<synchronizer>::_TNTC<_Dummy>::_NonNestedTuple<tuple<_Tail ...>&&>()), bool>::type <anonymous> > std::tuple<_Elements>::tuple(std::allocator_arg_t, const _Alloc&, std::tuple<_Args2 ...>&&)’
793 | tuple(allocator_arg_t __tag, const _Alloc& __a,
| ^~~~~
/usr/include/c++/9/tuple:793:2: note: template argument deduction/substitution failed:
In file included from server.cpp:14:
/usr/include/c++/9/thread:267:4: note: candidate expects 3 arguments, 1 provided
267 | } };
| ^
In file included from /usr/include/c++/9/bits/unique_ptr.h:37,
from /usr/include/c++/9/memory:80,
from /usr/include/c++/9/thread:39,
from server.cpp:14:
/usr/include/c++/9/tuple:778:11: note: candidate: ‘template<class _Alloc, class _Dummy, class ... _UElements, typename std::enable_if<((std::tuple<synchronizer>::_TMCT<_UElements ...>::_ConstructibleTuple<_UElements ...>() && (! std::tuple<synchronizer>::_TMCT<_UElements ...>::_ImplicitlyConvertibleTuple<_UElements ...>())) && std::tuple<synchronizer>::_TNTC<_Dummy>::_NonNestedTuple<const tuple<_Tail ...>&>()), bool>::type <anonymous> > std::tuple<_Elements>::tuple(std::allocator_arg_t, const _Alloc&, const std::tuple<_Args2 ...>&)’
778 | explicit tuple(allocator_arg_t __tag, const _Alloc& __a,
| ^~~~~
/usr/include/c++/9/tuple:778:11: note: template argument deduction/substitution failed:
In file included from server.cpp:14:
/usr/include/c++/9/thread:267:4: note: candidate expects 3 arguments, 1 provided
267 | } };
| ^
In file included from /usr/include/c++/9/bits/unique_ptr.h:37,
from /usr/include/c++/9/memory:80,
from /usr/include/c++/9/thread:39,
from server.cpp:14:
/usr/include/c++/9/tuple:763:2: note: candidate: ‘template<class _Alloc, class _Dummy, class ... _UElements, typename std::enable_if<((std::tuple<synchronizer>::_TMCT<_UElements ...>::_ConstructibleTuple<_UElements ...>() && std::tuple<synchronizer>::_TMCT<_UElements ...>::_ImplicitlyConvertibleTuple<_UElements ...>()) && std::tuple<synchronizer>::_TNTC<_Dummy>::_NonNestedTuple<const tuple<_Tail ...>&>()), bool>::type <anonymous> > std::tuple<_Elements>::tuple(std::allocator_arg_t, const _Alloc&, const std::tuple<_Args2 ...>&)’
763 | tuple(allocator_arg_t __tag, const _Alloc& __a,
| ^~~~~
/usr/include/c++/9/tuple:763:2: note: template argument deduction/substitution failed:
In file included from server.cpp:14:
/usr/include/c++/9/thread:267:4: note: candidate expects 3 arguments, 1 provided
267 | } };
| ^
In file included from /usr/include/c++/9/bits/unique_ptr.h:37,
from /usr/include/c++/9/memory:80,
from /usr/include/c++/9/thread:39,
from server.cpp:14:
/usr/include/c++/9/tuple:751:2: note: candidate: ‘template<class _Alloc> std::tuple<_Elements>::tuple(std::allocator_arg_t, const _Alloc&, std::tuple<_Elements>&&)’
751 | tuple(allocator_arg_t __tag, const _Alloc& __a, tuple&& __in)
| ^~~~~
/usr/include/c++/9/tuple:751:2: note: template argument deduction/substitution failed:
In file included from server.cpp:14:
/usr/include/c++/9/thread:267:4: note: candidate expects 3 arguments, 1 provided
267 | } };
| ^
In file included from /usr/include/c++/9/bits/unique_ptr.h:37,
from /usr/include/c++/9/memory:80,
from /usr/include/c++/9/thread:39,
from server.cpp:14:
/usr/include/c++/9/tuple:747:2: note: candidate: ‘template<class _Alloc> std::tuple<_Elements>::tuple(std::allocator_arg_t, const _Alloc&, const std::tuple<_Elements>&)’
747 | tuple(allocator_arg_t __tag, const _Alloc& __a, const tuple& __in)
| ^~~~~
/usr/include/c++/9/tuple:747:2: note: template argument deduction/substitution failed:
In file included from server.cpp:14:
/usr/include/c++/9/thread:267:4: note: candidate expects 3 arguments, 1 provided
267 | } };
| ^
In file included from /usr/include/c++/9/bits/unique_ptr.h:37,
from /usr/include/c++/9/memory:80,
from /usr/include/c++/9/thread:39,
from server.cpp:14:
/usr/include/c++/9/tuple:741:11: note: candidate: ‘template<class _Alloc, class ... _UElements, typename std::enable_if<(std::tuple<synchronizer>::_TMC<_UElements ...>::_MoveConstructibleTuple<_UElements ...>() && (! std::tuple<synchronizer>::_TMC<_UElements ...>::_ImplicitlyMoveConvertibleTuple<_UElements ...>())), bool>::type <anonymous> > std::tuple<_Elements>::tuple(std::allocator_arg_t, const _Alloc&, _UElements&& ...)’
741 | explicit tuple(allocator_arg_t __tag, const _Alloc& __a,
| ^~~~~
/usr/include/c++/9/tuple:741:11: note: template argument deduction/substitution failed:
In file included from server.cpp:14:
/usr/include/c++/9/thread:267:4: note: candidate expects at least 2 arguments, 1 provided
267 | } };
| ^
In file included from /usr/include/c++/9/bits/unique_ptr.h:37,
from /usr/include/c++/9/memory:80,
from /usr/include/c++/9/thread:39,
from server.cpp:14:
/usr/include/c++/9/tuple:730:2: note: candidate: ‘template<class _Alloc, class ... _UElements, typename std::enable_if<(std::tuple<synchronizer>::_TMC<_UElements ...>::_MoveConstructibleTuple<_UElements ...>() && std::tuple<synchronizer>::_TMC<_UElements ...>::_ImplicitlyMoveConvertibleTuple<_UElements ...>()), bool>::type <anonymous> > std::tuple<_Elements>::tuple(std::allocator_arg_t, const _Alloc&, _UElements&& ...)’
730 | tuple(allocator_arg_t __tag, const _Alloc& __a,
| ^~~~~
/usr/include/c++/9/tuple:730:2: note: template argument deduction/substitution failed:
In file included from server.cpp:14:
/usr/include/c++/9/thread:267:4: note: candidate expects at least 2 arguments, 1 provided
267 | } };
| ^
In file included from /usr/include/c++/9/bits/unique_ptr.h:37,
from /usr/include/c++/9/memory:80,
from /usr/include/c++/9/thread:39,
from server.cpp:14:
/usr/include/c++/9/tuple:720:11: note: candidate: ‘template<class _Alloc, class _Dummy, typename std::enable_if<(std::tuple<synchronizer>::_TCC<_Dummy>::_ConstructibleTuple<synchronizer>() && (! std::tuple<synchronizer>::_TCC<_Dummy>::_ImplicitlyConvertibleTuple<synchronizer>())), bool>::type <anonymous> > std::tuple<_Elements>::tuple(std::allocator_arg_t, const _Alloc&, const _Elements& ...)’
720 | explicit tuple(allocator_arg_t __tag, const _Alloc& __a,
| ^~~~~
/usr/include/c++/9/tuple:720:11: note: template argument deduction/substitution failed:
In file included from server.cpp:14:
/usr/include/c++/9/thread:267:4: note: candidate expects 3 arguments, 1 provided
267 | } };
| ^
In file included from /usr/include/c++/9/bits/unique_ptr.h:37,
from /usr/include/c++/9/memory:80,
from /usr/include/c++/9/thread:39,
from server.cpp:14:
/usr/include/c++/9/tuple:709:2: note: candidate: ‘template<class _Alloc, class _Dummy, typename std::enable_if<(std::tuple<synchronizer>::_TCC<_Dummy>::_ConstructibleTuple<synchronizer>() && std::tuple<synchronizer>::_TCC<_Dummy>::_ImplicitlyConvertibleTuple<synchronizer>()), bool>::type <anonymous> > std::tuple<_Elements>::tuple(std::allocator_arg_t, const _Alloc&, const _Elements& ...)’
709 | tuple(allocator_arg_t __tag, const _Alloc& __a,
| ^~~~~
/usr/include/c++/9/tuple:709:2: note: template argument deduction/substitution failed:
In file included from server.cpp:14:
/usr/include/c++/9/thread:267:4: note: candidate expects 3 arguments, 1 provided
267 | } };
| ^
In file included from /usr/include/c++/9/bits/unique_ptr.h:37,
from /usr/include/c++/9/memory:80,
from /usr/include/c++/9/thread:39,
from server.cpp:14:
/usr/include/c++/9/tuple:699:2: note: candidate: ‘template<class _Alloc> std::tuple<_Elements>::tuple(std::allocator_arg_t, const _Alloc&)’
699 | tuple(allocator_arg_t __tag, const _Alloc& __a)
| ^~~~~
/usr/include/c++/9/tuple:699:2: note: template argument deduction/substitution failed:
In file included from server.cpp:14:
/usr/include/c++/9/thread:267:4: note: candidate expects 2 arguments, 1 provided
267 | } };
| ^
In file included from /usr/include/c++/9/bits/unique_ptr.h:37,
from /usr/include/c++/9/memory:80,
from /usr/include/c++/9/thread:39,
from server.cpp:14:
/usr/include/c++/9/tuple:693:28: note: candidate: ‘template<class ... _UElements, class _Dummy, typename std::enable_if<((std::tuple<synchronizer>::_TMCT<_UElements ...>::_MoveConstructibleTuple<_UElements ...>() && (! std::tuple<synchronizer>::_TMCT<_UElements ...>::_ImplicitlyMoveConvertibleTuple<_UElements ...>())) && std::tuple<synchronizer>::_TNTC<_Dummy>::_NonNestedTuple<tuple<_Tps ...>&&>()), bool>::type <anonymous> > constexpr std::tuple<_Elements>::tuple(std::tuple<_Args1 ...>&&)’
693 | explicit constexpr tuple(tuple<_UElements...>&& __in)
| ^~~~~
/usr/include/c++/9/tuple:693:28: note: template argument deduction/substitution failed:
In file included from server.cpp:14:
/usr/include/c++/9/thread:267:4: note: ‘synchronizer’ is not derived from ‘std::tuple<_Tps ...>’
267 | } };
| ^
In file included from /usr/include/c++/9/bits/unique_ptr.h:37,
from /usr/include/c++/9/memory:80,
from /usr/include/c++/9/thread:39,
from server.cpp:14:
/usr/include/c++/9/tuple:682:19: note: candidate: ‘template<class ... _UElements, class _Dummy, typename std::enable_if<((std::tuple<synchronizer>::_TMCT<_UElements ...>::_MoveConstructibleTuple<_UElements ...>() && std::tuple<synchronizer>::_TMCT<_UElements ...>::_ImplicitlyMoveConvertibleTuple<_UElements ...>()) && std::tuple<synchronizer>::_TNTC<_Dummy>::_NonNestedTuple<tuple<_Tps ...>&&>()), bool>::type <anonymous> > constexpr std::tuple<_Elements>::tuple(std::tuple<_Args1 ...>&&)’
682 | constexpr tuple(tuple<_UElements...>&& __in)
| ^~~~~
/usr/include/c++/9/tuple:682:19: note: template argument deduction/substitution failed:
In file included from server.cpp:14:
/usr/include/c++/9/thread:267:4: note: ‘synchronizer’ is not derived from ‘std::tuple<_Tps ...>’
267 | } };
| ^
In file included from /usr/include/c++/9/bits/unique_ptr.h:37,
from /usr/include/c++/9/memory:80,
from /usr/include/c++/9/thread:39,
from server.cpp:14:
/usr/include/c++/9/tuple:670:28: note: candidate: ‘template<class ... _UElements, class _Dummy, typename std::enable_if<((std::tuple<synchronizer>::_TMCT<_UElements ...>::_ConstructibleTuple<_UElements ...>() && (! std::tuple<synchronizer>::_TMCT<_UElements ...>::_ImplicitlyConvertibleTuple<_UElements ...>())) && std::tuple<synchronizer>::_TNTC<_Dummy>::_NonNestedTuple<const tuple<_Tps ...>&>()), bool>::type <anonymous> > constexpr std::tuple<_Elements>::tuple(const std::tuple<_Args1 ...>&)’
670 | explicit constexpr tuple(const tuple<_UElements...>& __in)
| ^~~~~
/usr/include/c++/9/tuple:670:28: note: template argument deduction/substitution failed:
In file included from server.cpp:14:
/usr/include/c++/9/thread:267:4: note: ‘synchronizer’ is not derived from ‘const std::tuple<_Tps ...>’
267 | } };
| ^
In file included from /usr/include/c++/9/bits/unique_ptr.h:37,
from /usr/include/c++/9/memory:80,
from /usr/include/c++/9/thread:39,
from server.cpp:14:
/usr/include/c++/9/tuple:658:19: note: candidate: ‘template<class ... _UElements, class _Dummy, typename std::enable_if<((std::tuple<synchronizer>::_TMCT<_UElements ...>::_ConstructibleTuple<_UElements ...>() && std::tuple<synchronizer>::_TMCT<_UElements ...>::_ImplicitlyConvertibleTuple<_UElements ...>()) && std::tuple<synchronizer>::_TNTC<_Dummy>::_NonNestedTuple<const tuple<_Tps ...>&>()), bool>::type <anonymous> > constexpr std::tuple<_Elements>::tuple(const std::tuple<_Args1 ...>&)’
658 | constexpr tuple(const tuple<_UElements...>& __in)
| ^~~~~
/usr/include/c++/9/tuple:658:19: note: template argument deduction/substitution failed:
In file included from server.cpp:14:
/usr/include/c++/9/thread:267:4: note: ‘synchronizer’ is not derived from ‘const std::tuple<_Tps ...>’
267 | } };
| ^
In file included from /usr/include/c++/9/bits/unique_ptr.h:37,
from /usr/include/c++/9/memory:80,
from /usr/include/c++/9/thread:39,
from server.cpp:14:
/usr/include/c++/9/tuple:642:17: note: candidate: ‘constexpr std::tuple<_Elements>::tuple(std::tuple<_Elements>&&) [with _Elements = {synchronizer}]’
642 | constexpr tuple(tuple&&) = default;
| ^~~~~
/usr/include/c++/9/tuple:642:23: note: no known conversion for argument 1 from ‘synchronizer’ to ‘std::tuple<synchronizer>&&’
642 | constexpr tuple(tuple&&) = default;
| ^~~~~~~
/usr/include/c++/9/tuple:637:28: note: candidate: ‘template<class ... _UElements, typename std::enable_if<((std::tuple<synchronizer>::_TMC<_UElements ...>::_MoveConstructibleTuple<_UElements ...>() && (! std::tuple<synchronizer>::_TMC<_UElements ...>::_ImplicitlyMoveConvertibleTuple<_UElements ...>())) && (1 >= 1)), bool>::type <anonymous> > constexpr std::tuple<_Elements>::tuple(_UElements&& ...)’
637 | explicit constexpr tuple(_UElements&&... __elements)
| ^~~~~
/usr/include/c++/9/tuple:637:28: note: template argument deduction/substitution failed:
/usr/include/c++/9/tuple:636:21: error: no type named ‘type’ in ‘struct std::enable_if<false, bool>’
636 | bool>::type=false>
| ^~~~~
/usr/include/c++/9/tuple:626:19: note: candidate: ‘template<class ... _UElements, typename std::enable_if<((std::tuple<synchronizer>::_TMC<_UElements ...>::_MoveConstructibleTuple<_UElements ...>() && std::tuple<synchronizer>::_TMC<_UElements ...>::_ImplicitlyMoveConvertibleTuple<_UElements ...>()) && (1 >= 1)), bool>::type <anonymous> > constexpr std::tuple<_Elements>::tuple(_UElements&& ...)’
626 | constexpr tuple(_UElements&&... __elements)
| ^~~~~
/usr/include/c++/9/tuple:626:19: note: template argument deduction/substitution failed:
/usr/include/c++/9/tuple:625:21: error: no type named ‘type’ in ‘struct std::enable_if<false, bool>’
625 | bool>::type=true>
| ^~~~
/usr/include/c++/9/tuple:599:26: note: candidate: ‘template<class _Dummy, typename std::enable_if<((std::tuple<synchronizer>::_TCC<_Dummy>::_ConstructibleTuple<synchronizer>() && (! std::tuple<synchronizer>::_TCC<_Dummy>::_ImplicitlyConvertibleTuple<synchronizer>())) && (1 >= 1)), bool>::type <anonymous> > constexpr std::tuple<_Elements>::tuple(const _Elements& ...)’
599 | explicit constexpr tuple(const _Elements&... __elements)
| ^~~~~
/usr/include/c++/9/tuple:599:26: note: template argument deduction/substitution failed:
/usr/include/c++/9/tuple:598:28: error: no type named ‘type’ in ‘struct std::enable_if<false, bool>’
598 | bool>::type=false>
| ^~~~~
/usr/include/c++/9/tuple:588:19: note: candidate: ‘template<class _Dummy, typename std::enable_if<((std::tuple<synchronizer>::_TCC<_Dummy>::_ConstructibleTuple<synchronizer>() && std::tuple<synchronizer>::_TCC<_Dummy>::_ImplicitlyConvertibleTuple<synchronizer>()) && (1 >= 1)), bool>::type <anonymous> > constexpr std::tuple<_Elements>::tuple(const _Elements& ...)’
588 | constexpr tuple(const _Elements&... __elements)
| ^~~~~
/usr/include/c++/9/tuple:588:19: note: template argument deduction/substitution failed:
/usr/include/c++/9/tuple:587:28: error: no type named ‘type’ in ‘struct std::enable_if<false, bool>’
587 | bool>::type=true>
| ^~~~
/usr/include/c++/9/tuple:571:26: note: candidate: ‘template<class _Dummy, typename std::enable_if<(std::tuple<synchronizer>::_TC2<_Dummy>::_DefaultConstructibleTuple() && (! std::tuple<synchronizer>::_TC2<_Dummy>::_ImplicitlyDefaultConstructibleTuple())), bool>::type <anonymous> > constexpr std::tuple<_Elements>::tuple()’
571 | explicit constexpr tuple()
| ^~~~~
/usr/include/c++/9/tuple:571:26: note: template argument deduction/substitution failed:
In file included from server.cpp:14:
/usr/include/c++/9/thread:267:4: note: candidate expects 0 arguments, 1 provided
267 | } };
| ^
In file included from /usr/include/c++/9/bits/unique_ptr.h:37,
from /usr/include/c++/9/memory:80,
from /usr/include/c++/9/thread:39,
from server.cpp:14:
/usr/include/c++/9/tuple:561:17: note: candidate: ‘template<class _Dummy, typename std::enable_if<std::tuple<synchronizer>::_TC2<_Dummy>::_ImplicitlyDefaultConstructibleTuple(), bool>::type <anonymous> > constexpr std::tuple<_Elements>::tuple()’
561 | constexpr tuple()
| ^~~~~
/usr/include/c++/9/tuple:561:17: note: template argument deduction/substitution failed:
In file included from server.cpp:14:
/usr/include/c++/9/thread:267:4: note: candidate expects 0 arguments, 1 provided
267 | } };
| ^
/usr/include/c++/9/thread:267:4: error: could not convert ‘{<expression error>}’ from ‘<brace-enclosed initializer list>’ to ‘std::thread::_Invoker<std::tuple<synchronizer> >’

Boost.Iostreams stream can't forward 4 constructor arguments

I'm trying to create a custom std::istream using Boost's stream class. It's supposed to be easier than manually creating a std::streambuf but I've hit a roadblock.
Constructing a stream requires a source object. stream has a set of forwarding constructors that take a list of arguments and forward them to the source's constructor—that's what I want to use.
// Forwarding constructors
template<typename U>
stream([const] U& u);
template<typename U1, typename U2>
stream([const] U1& u1, const U2& u2);
...
template<typename U1, ..., typename UN>
stream([const] U1& u1, const U2& u2, ..., const UN& uN);
Each of these members constructs an instance of stream and associates it with an instance of the Device T constructed from the given lists of arguments. The T constructors involved must take all arguments by value or const reference.
It works great with three arguments:
#include <memory>
#include <boost/iostreams/stream.hpp>
namespace io = boost::iostreams;
class IoSource: public io::source {
public:
IoSource(int foo, int bar, int baz) { }
std::streamsize read(char *buffer, std::streamsize n) { return -1; }
};
int main() {
auto stream = std::make_unique<io::stream<IoSource>>(1, 2, 3);
}
Result:
$ g++ -Wall -Werror -std=gnu++20 test.cpp -o /dev/null
The same program with four arguments fails to compile with a barrage of template errors:
#include <memory>
#include <boost/iostreams/stream.hpp>
namespace io = boost::iostreams;
class IoSource: public io::source {
public:
IoSource(int foo, int bar, int baz, int quux) { }
std::streamsize read(char *buffer, std::streamsize n) { return -1; }
};
int main() {
auto stream = std::make_unique<io::stream<IoSource>>(1, 2, 3, 4);
}
Result:
$ g++ -Wall -Werror -std=gnu++20 test.cpp -o /dev/null
In file included from /usr/include/c++/10/memory:83,
from test.cpp:1:
/usr/include/c++/10/bits/unique_ptr.h: In instantiation of 'typename std::_MakeUniq<_Tp>::__single_object std::make_unique(_Args&& ...) [with _Tp = boost::iostreams::stream<IoSource>; _Args = {int, int, int, int}; typename std::_MakeUniq<_Tp>::__single_object = std::unique_ptr<boost::iostreams::stream<IoSource>, std::default_delete<boost::iostreams::stream<IoSource> > >]':
test.cpp:14:68: required from here
/usr/include/c++/10/bits/unique_ptr.h:962:30: error: no matching function for call to 'boost::iostreams::stream<IoSource>::stream(int, int, int, int)'
962 | { return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/boost/preprocessor/tuple/elem.hpp:23,
from /usr/include/boost/preprocessor/repetition/enum_binary_params.hpp:19,
from /usr/include/boost/iostreams/detail/forward.hpp:22,
from /usr/include/boost/iostreams/stream.hpp:18,
from test.cpp:3:
/usr/include/boost/iostreams/stream.hpp:144:5: note: candidate: 'boost::iostreams::stream<Device, Tr, Alloc>::stream(U100&, const U0&, const U1&, typename boost::disable_if<boost::is_same<U0, T> >::type*) [with U100 = int; U0 = int; U1 = int; Device = IoSource; Tr = std::char_traits<char>; Alloc = std::allocator<char>; typename boost::disable_if<boost::is_same<U0, T> >::type = void]' (near match)
144 | BOOST_IOSTREAMS_FORWARD( stream, open_impl, Device,
| ^~~~~~~~~~~~~~~~~~~~~~~
/usr/include/boost/iostreams/stream.hpp:144:5: note: conversion of argument 4 would be ill-formed:
In file included from /usr/include/c++/10/memory:83,
from test.cpp:1:
/usr/include/c++/10/bits/unique_ptr.h:962:30: error: invalid conversion from 'int' to 'boost::disable_if_c<false, void>::type*' {aka 'void*'} [-fpermissive]
962 | { return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| |
| int
In file included from /usr/include/boost/preprocessor/tuple/elem.hpp:23,
from /usr/include/boost/preprocessor/repetition/enum_binary_params.hpp:19,
from /usr/include/boost/iostreams/detail/forward.hpp:22,
from /usr/include/boost/iostreams/stream.hpp:18,
from test.cpp:3:
/usr/include/boost/iostreams/stream.hpp:144:5: note: candidate: 'boost::iostreams::stream<Device, Tr, Alloc>::stream(const U0&, const U1&, const U2&, typename boost::disable_if<boost::is_same<U0, T> >::type*) [with U0 = int; U1 = int; U2 = int; Device = IoSource; Tr = std::char_traits<char>; Alloc = std::allocator<char>; typename boost::disable_if<boost::is_same<U0, T> >::type = void]' (near match)
144 | BOOST_IOSTREAMS_FORWARD( stream, open_impl, Device,
| ^~~~~~~~~~~~~~~~~~~~~~~
/usr/include/boost/iostreams/stream.hpp:144:5: note: conversion of argument 4 would be ill-formed:
In file included from /usr/include/c++/10/memory:83,
from test.cpp:1:
/usr/include/c++/10/bits/unique_ptr.h:962:30: error: invalid conversion from 'int' to 'boost::disable_if_c<false, void>::type*' {aka 'void*'} [-fpermissive]
962 | { return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| |
| int
In file included from /usr/include/boost/preprocessor/tuple/elem.hpp:23,
from /usr/include/boost/preprocessor/repetition/enum_binary_params.hpp:19,
from /usr/include/boost/iostreams/detail/forward.hpp:22,
from /usr/include/boost/iostreams/stream.hpp:18,
from test.cpp:3:
/usr/include/boost/iostreams/stream.hpp:144:5: note: candidate: 'template<class U100, class U0> boost::iostreams::stream<Device, Tr, Alloc>::stream(U100&, const U0&, typename boost::disable_if<boost::is_same<U0, T> >::type*) [with U100 = U100; U0 = U0; Device = IoSource; Tr = std::char_traits<char>; Alloc = std::allocator<char>]'
144 | BOOST_IOSTREAMS_FORWARD( stream, open_impl, Device,
| ^~~~~~~~~~~~~~~~~~~~~~~
/usr/include/boost/iostreams/stream.hpp:144:5: note: template argument deduction/substitution failed:
In file included from /usr/include/c++/10/memory:83,
from test.cpp:1:
/usr/include/c++/10/bits/unique_ptr.h:962:30: note: candidate expects 3 arguments, 4 provided
962 | { return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/boost/preprocessor/tuple/elem.hpp:23,
from /usr/include/boost/preprocessor/repetition/enum_binary_params.hpp:19,
from /usr/include/boost/iostreams/detail/forward.hpp:22,
from /usr/include/boost/iostreams/stream.hpp:18,
from test.cpp:3:
/usr/include/boost/iostreams/stream.hpp:144:5: note: candidate: 'template<class U0, class U1> boost::iostreams::stream<Device, Tr, Alloc>::stream(const U0&, const U1&, typename boost::disable_if<boost::is_same<U0, T> >::type*) [with U0 = U0; U1 = U1; Device = IoSource; Tr = std::char_traits<char>; Alloc = std::allocator<char>]'
144 | BOOST_IOSTREAMS_FORWARD( stream, open_impl, Device,
| ^~~~~~~~~~~~~~~~~~~~~~~
/usr/include/boost/iostreams/stream.hpp:144:5: note: template argument deduction/substitution failed:
In file included from /usr/include/c++/10/memory:83,
from test.cpp:1:
/usr/include/c++/10/bits/unique_ptr.h:962:30: note: candidate expects 3 arguments, 4 provided
962 | { return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/boost/preprocessor/tuple/elem.hpp:23,
from /usr/include/boost/preprocessor/repetition/enum_binary_params.hpp:19,
from /usr/include/boost/iostreams/detail/forward.hpp:22,
from /usr/include/boost/iostreams/stream.hpp:18,
from test.cpp:3:
/usr/include/boost/iostreams/stream.hpp:144:5: note: candidate: 'template<class U100> boost::iostreams::stream<Device, Tr, Alloc>::stream(U100&, typename boost::disable_if<boost::is_same<U0, T> >::type*) [with U100 = U100; Device = IoSource; Tr = std::char_traits<char>; Alloc = std::allocator<char>]'
144 | BOOST_IOSTREAMS_FORWARD( stream, open_impl, Device,
| ^~~~~~~~~~~~~~~~~~~~~~~
/usr/include/boost/iostreams/stream.hpp:144:5: note: template argument deduction/substitution failed:
In file included from /usr/include/c++/10/memory:83,
from test.cpp:1:
/usr/include/c++/10/bits/unique_ptr.h:962:30: note: candidate expects 2 arguments, 4 provided
962 | { return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/boost/preprocessor/tuple/elem.hpp:23,
from /usr/include/boost/preprocessor/repetition/enum_binary_params.hpp:19,
from /usr/include/boost/iostreams/detail/forward.hpp:22,
from /usr/include/boost/iostreams/stream.hpp:18,
from test.cpp:3:
/usr/include/boost/iostreams/stream.hpp:144:5: note: candidate: 'template<class U0> boost::iostreams::stream<Device, Tr, Alloc>::stream(const U0&, typename boost::disable_if<boost::is_same<U0, T> >::type*) [with U0 = U0; Device = IoSource; Tr = std::char_traits<char>; Alloc = std::allocator<char>]'
144 | BOOST_IOSTREAMS_FORWARD( stream, open_impl, Device,
| ^~~~~~~~~~~~~~~~~~~~~~~
/usr/include/boost/iostreams/stream.hpp:144:5: note: template argument deduction/substitution failed:
In file included from /usr/include/c++/10/memory:83,
from test.cpp:1:
/usr/include/c++/10/bits/unique_ptr.h:962:30: note: candidate expects 2 arguments, 4 provided
962 | { return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/boost/iostreams/stream.hpp:18,
from test.cpp:3:
/usr/include/boost/iostreams/stream.hpp:144:5: note: candidate: 'boost::iostreams::stream<Device, Tr, Alloc>::stream(const boost::reference_wrapper<T>&, std::streamsize, std::streamsize) [with Device = IoSource; Tr = std::char_traits<char>; Alloc = std::allocator<char>; std::streamsize = long int]'
144 | BOOST_IOSTREAMS_FORWARD( stream, open_impl, Device,
| ^~~~~~~~~~~~~~~~~~~~~~~
/usr/include/boost/iostreams/stream.hpp:144:5: note: candidate expects 3 arguments, 4 provided
/usr/include/boost/iostreams/stream.hpp:144:5: note: candidate: 'boost::iostreams::stream<Device, Tr, Alloc>::stream(Device&, std::streamsize, std::streamsize) [with Device = IoSource; Tr = std::char_traits<char>; Alloc = std::allocator<char>; std::streamsize = long int]'
144 | BOOST_IOSTREAMS_FORWARD( stream, open_impl, Device,
| ^~~~~~~~~~~~~~~~~~~~~~~
/usr/include/boost/iostreams/stream.hpp:144:5: note: candidate expects 3 arguments, 4 provided
/usr/include/boost/iostreams/stream.hpp:144:5: note: candidate: 'boost::iostreams::stream<Device, Tr, Alloc>::stream(const Device&, std::streamsize, std::streamsize) [with Device = IoSource; Tr = std::char_traits<char>; Alloc = std::allocator<char>; std::streamsize = long int]'
144 | BOOST_IOSTREAMS_FORWARD( stream, open_impl, Device,
| ^~~~~~~~~~~~~~~~~~~~~~~
/usr/include/boost/iostreams/stream.hpp:144:5: note: candidate expects 3 arguments, 4 provided
In file included from test.cpp:3:
/usr/include/boost/iostreams/stream.hpp:143:5: note: candidate: 'boost::iostreams::stream<Device, Tr, Alloc>::stream() [with Device = IoSource; Tr = std::char_traits<char>; Alloc = std::allocator<char>]'
143 | stream() { }
| ^~~~~~
/usr/include/boost/iostreams/stream.hpp:143:5: note: candidate expects 0 arguments, 4 provided
What am I doing wrong? I believe I've narrowed this down to the core problem. It seems to be purely an issue with the number of arguments; it doesn't matter if they're values or const references, nor do their types matter.
Boost 1.78
g++ (Debian 10.2.1-6) 10.2.1 20210110
Boost doesn't use variadic templates and perfect forwarding to forward arguments. It uses preprocessor macros to simulate varargs, macros which have a maximum arity:
boost/iostreams/detail/config/limits.hpp
#ifndef BOOST_IOSTREAMS_MAX_FORWARDING_ARITY
# define BOOST_IOSTREAMS_MAX_FORWARDING_ARITY 3
#endif
The number of arguments can be increased by defining the macro above before including Boost's headers:
#define BOOST_IOSTREAMS_MAX_FORWARDING_ARITY 4
#include <boost/iostreams/stream.hpp>

Read file and split and trim each line with cpp20

I have created a code that fits my specific need - to split the string (read from a file) at comma stripping any whitespaces. also, I want to convert these substrings to double and store them in std::vector.
I use ranges library in c++20 and implement like bellow:
#include <iostream>
#include <fstream>
#include <ranges>
#include <algorithm>
#include <vector>
auto join_character_in_each_subranges = [](auto &&rng) { return std::string_view(&*rng.begin(), std::ranges::distance(rng)); };
auto trimming = std::ranges::views::filter([](auto character){ return !std::isspace(character);});
typedef std::vector<double> LineList;
typedef std::vector<LineList> List;
int main () {
std::ifstream myfile;
std::string myline;
List list;
myfile.open("data.txt");
while (std::getline(myfile, myline))
{
LineList line_list;
for (auto words : myline
| std::ranges::views::split(',')
| std::ranges::views::transform(join_character_in_each_subranges))
{
auto words_trimming = words | trimming;
std::string clean_numbers;
std::ranges::for_each(words_trimming, [&](auto character){ clean_numbers += character;});
line_list.push_back(atof(clean_numbers.c_str()));
}
list.push_back(line_list);
}
}
First, iterate on myline sentences and splits the view into subranges on the delimiter
myline | std::ranges::views::split(',')
append each character inside subranges and view into the std::string with transform function
std::transform applies the given function to a range and stores the result in another range.
std::ranges::views::transform(join_character_in_each_subranges)
also, remove any prefix and suffix from view ranges
auto words_trimming = words | trimming;
and convert view ranges to std::string with
std::ranges::for_each(words_trimming, [&](auto character){ clean_number += character;});
finally, convert each clean_number to double and push_back into the list.
line_list.push_back(atof(clean_words.c_str()));
but when I change this code to
for (auto words : myline
| std::ranges::views::split(',')
| std::ranges::views::transform(join_character_in_each_subranges)
| trimming)
I've got a lot of errors.
read.cpp:26:17: error: no match for 'operator|' (operand types are 'std::ranges::transform_view<std::ranges::split_view<std::ranges::ref_view<std::__cxx11::basic_string<char> >, std::ranges::single_view<char> >, <lambda(auto:16&&)> >' and 'std::ranges::views::__adaptor::_RangeAdaptorClosure<std::ranges::views::__adaptor::_RangeAdaptor<_Callable>::operator()<{<lambda(auto:17&&)>}>::<lambda(_Range&&)> >')
23 | for (auto words : myline
| ~~~~~~
24 | | std::ranges::views::split(',')
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
25 | | std::ranges::views::transform(join_character_in_each_subranges)
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| |
| std::ranges::transform_view<std::ranges::split_view<std::ranges::ref_view<std::__cxx11::basic_string<char> >, std::ranges::single_view<char> >, <lambda(auto:16&&)> >
26 | | trimming)
| ^ ~~~~~~~~
| |
| std::ranges::views::__adaptor::_RangeAdaptorClosure<std::ranges::views::__adaptor::_RangeAdaptor<_Callable>::operator()<{<lambda(auto:17&&)>}>::<lambda(_Range&&)> >
In file included from read.cpp:3:
/usr/local/Cellar/gcc#10/10.3.0/include/c++/10.3.0/ranges:1183:4: note: candidate: 'template<class _Tp> constexpr auto std::ranges::views::__adaptor::operator|(const std::ranges::views::__adaptor::_RangeAdaptorClosure<_Callable>&, const std::ranges::views::__adaptor::_RangeAdaptorClosure<std::ranges::views::__adaptor::_RangeAdaptor<_Callable>::operator()<{<lambda(auto:17&&)>}>::<lambda(_Range&&)> >&)'
1183 | operator|(const _RangeAdaptorClosure<_Tp>& __x,
| ^~~~~~~~
/usr/local/Cellar/gcc#10/10.3.0/include/c++/10.3.0/ranges:1183:4: note: template argument deduction/substitution failed:
read.cpp:26:19: note: 'std::ranges::transform_view<std::ranges::split_view<std::ranges::ref_view<std::__cxx11::basic_string<char> >, std::ranges::single_view<char> >, <lambda(auto:16&&)> >' is not derived from 'const std::ranges::views::__adaptor::_RangeAdaptorClosure<_Callable>'
26 | | trimming)
| ^~~~~~~~
In file included from read.cpp:3:
/usr/local/Cellar/gcc#10/10.3.0/include/c++/10.3.0/ranges:1178:4: note: candidate: 'constexpr auto std::ranges::views::__adaptor::operator|(_Range&&, const std::ranges::views::__adaptor::_RangeAdaptorClosure<_Callable>&) [with _Range = std::ranges::transform_view<std::ranges::split_view<std::ranges::ref_view<std::__cxx11::basic_string<char> >, std::ranges::single_view<char> >, <lambda(auto:16&&)> >; _Callable = std::ranges::views::__adaptor::_RangeAdaptor<_Callable>::operator()<{<lambda(auto:17&&)>}>::<lambda(_Range&&)>]'
1178 | operator|(_Range&& __r, const _RangeAdaptorClosure& __o)
| ^~~~~~~~
/usr/local/Cellar/gcc#10/10.3.0/include/c++/10.3.0/ranges:1178:4: note: constraints not satisfied
/usr/local/Cellar/gcc#10/10.3.0/include/c++/10.3.0/ranges: In instantiation of 'constexpr auto std::ranges::views::__adaptor::operator|(_Range&&, const std::ranges::views::__adaptor::_RangeAdaptorClosure<_Callable>&) [with _Range = std::ranges::transform_view<std::ranges::split_view<std::ranges::ref_view<std::__cxx11::basic_string<char> >, std::ranges::single_view<char> >, <lambda(auto:16&&)> >; _Callable = std::ranges::views::__adaptor::_RangeAdaptor<_Callable>::operator()<{<lambda(auto:17&&)>}>::<lambda(_Range&&)>]':
read.cpp:26:19: required from here
/usr/local/Cellar/gcc#10/10.3.0/include/c++/10.3.0/ranges:1178:4: required by the constraints of 'template<class _Callable> template<class _Range> requires (viewable_range<_Range>) && requires{(declval<_Callable>)()((declval<_Range>)());} constexpr auto std::ranges::views::__adaptor::operator|(_Range&&, const std::ranges::views::__adaptor::_RangeAdaptorClosure<_Callable>&)'
/usr/local/Cellar/gcc#10/10.3.0/include/c++/10.3.0/ranges:1176:13: in requirements [with _Range = std::ranges::transform_view<std::ranges::split_view<std::ranges::ref_view<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::ranges::single_view<char> >, ._anon_104>; _Callable = std::ranges::views::__adaptor::_RangeAdaptor<std::ranges::views::._anon_83>::operator()::._anon_106]
/usr/local/Cellar/gcc#10/10.3.0/include/c++/10.3.0/ranges:1176:44: note: the required expression 'declval<_Callable>()(declval<_Range>())' is invalid
1176 | requires requires { declval<_Callable>()(declval<_Range>()); }
| ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~
cc1plus: note: set '-fconcepts-diagnostics-depth=' to at least 2 for more detail
In file included from /usr/local/Cellar/gcc#10/10.3.0/include/c++/10.3.0/ios:42,
from /usr/local/Cellar/gcc#10/10.3.0/include/c++/10.3.0/ostream:38,
from /usr/local/Cellar/gcc#10/10.3.0/include/c++/10.3.0/iostream:39,
from read.cpp:1:
/usr/local/Cellar/gcc#10/10.3.0/include/c++/10.3.0/bits/ios_base.h:87:3: note: candidate: 'constexpr std::_Ios_Fmtflags std::operator|(std::_Ios_Fmtflags, std::_Ios_Fmtflags)'
87 | operator|(_Ios_Fmtflags __a, _Ios_Fmtflags __b)
| ^~~~~~~~
/usr/local/Cellar/gcc#10/10.3.0/include/c++/10.3.0/bits/ios_base.h:87:27: note: no known conversion for argument 1 from 'std::ranges::transform_view<std::ranges::split_view<std::ranges::ref_view<std::__cxx11::basic_string<char> >, std::ranges::single_view<char> >, <lambda(auto:16&&)> >' to 'std::_Ios_Fmtflags'
87 | operator|(_Ios_Fmtflags __a, _Ios_Fmtflags __b)
| ~~~~~~~~~~~~~~^~~
/usr/local/Cellar/gcc#10/10.3.0/include/c++/10.3.0/bits/ios_base.h:129:3: note: candidate: 'constexpr std::_Ios_Openmode std::operator|(std::_Ios_Openmode, std::_Ios_Openmode)'
129 | operator|(_Ios_Openmode __a, _Ios_Openmode __b)
| ^~~~~~~~
/usr/local/Cellar/gcc#10/10.3.0/include/c++/10.3.0/bits/ios_base.h:129:27: note: no known conversion for argument 1 from 'std::ranges::transform_view<std::ranges::split_view<std::ranges::ref_view<std::__cxx11::basic_string<char> >, std::ranges::single_view<char> >, <lambda(auto:16&&)> >' to 'std::_Ios_Openmode'
129 | operator|(_Ios_Openmode __a, _Ios_Openmode __b)
| ~~~~~~~~~~~~~~^~~
/usr/local/Cellar/gcc#10/10.3.0/include/c++/10.3.0/bits/ios_base.h:169:3: note: candidate: 'constexpr std::_Ios_Iostate std::operator|(std::_Ios_Iostate, std::_Ios_Iostate)'
169 | operator|(_Ios_Iostate __a, _Ios_Iostate __b)
| ^~~~~~~~
/usr/local/Cellar/gcc#10/10.3.0/include/c++/10.3.0/bits/ios_base.h:169:26: note: no known conversion for argument 1 from 'std::ranges::transform_view<std::ranges::split_view<std::ranges::ref_view<std::__cxx11::basic_string<char> >, std::ranges::single_view<char> >, <lambda(auto:16&&)> >' to 'std::_Ios_Iostate'
169 | operator|(_Ios_Iostate __a, _Ios_Iostate __b)
| ~~~~~~~~~~~~~^~~
how fix this problem?
Thanks
According to Frank's comment change trimming like this:
auto trimming = std::ranges::views::transform(std::ranges::views::filter([](char character){return !std::isspace(character); }));

Why type unsigned makes std::variant<int64_t,uint64_t> ambiguous to construct?

I'd like to use simply unsigned(123) and 123u rather than 123ul and (unsigned long)(123).
#include <cstdint>
#include <variant>
using namespace std;
int main()
{
using var_t = variant<int64_t,uint64_t,double>;
var_t v1{1};
var_t v2{1ul};
var_t v3{1u}; //ERROR
// var_t v4{unsigned(1)}; //ERROR
var_t v5{uint64_t(1)};
}
live example
Compiler output
g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
main.cpp: In function 'int main()':
main.cpp:10:15: error: no matching function for call to 'std::variant<long int, long unsigned int, double>::variant(<brace-enclosed initializer list>)'
10 | var_t v3{1u}; //ERROR
| ^
In file included from main.cpp:2:
/usr/local/include/c++/10.2.0/variant:1402:2: note: candidate: 'template<long unsigned int _Np, class _Up, class ... _Args, class _Tp, class> constexpr std::variant<_Types>::variant(std::in_place_index_t<_Np>, std::initializer_list<_Up>, _Args&& ...) [with long unsigned int _Np = _Np; _Up = _Up; _Args = {_Args ...}; _Tp = _Tp; <template-parameter-2-5> = <template-parameter-1-5>; _Types = {long int, long unsigned int, double}]'
1402 | variant(in_place_index_t<_Np>, initializer_list<_Up> __il,
| ^~~~~~~
/usr/local/include/c++/10.2.0/variant:1402:2: note: template argument deduction/substitution failed:
main.cpp:10:15: note: mismatched types 'std::in_place_index_t<_Idx>' and 'unsigned int'
10 | var_t v3{1u}; //ERROR
| ^
In file included from main.cpp:2:
/usr/local/include/c++/10.2.0/variant:1391:2: note: candidate: 'template<long unsigned int _Np, class ... _Args, class _Tp, class> constexpr std::variant<_Types>::variant(std::in_place_index_t<_Np>, _Args&& ...) [with long unsigned int _Np = _Np; _Args = {_Args ...}; _Tp = _Tp; <template-parameter-2-4> = <template-parameter-1-4>; _Types = {long int, long unsigned int, double}]'
1391 | variant(in_place_index_t<_Np>, _Args&&... __args)
| ^~~~~~~
/usr/local/include/c++/10.2.0/variant:1391:2: note: template argument deduction/substitution failed:
main.cpp:10:15: note: mismatched types 'std::in_place_index_t<_Idx>' and 'unsigned int'
10 | var_t v3{1u}; //ERROR
| ^
In file included from main.cpp:2:
/usr/local/include/c++/10.2.0/variant:1381:2: note: candidate: 'template<class _Tp, class _Up, class ... _Args, class> constexpr std::variant<_Types>::variant(std::in_place_type_t<_Tp>, std::initializer_list<_Up>, _Args&& ...) [with _Tp = _Tp; _Up = _Up; _Args = {_Args ...}; <template-parameter-2-4> = <template-parameter-1-4>; _Types = {long int, long unsigned int, double}]'
1381 | variant(in_place_type_t<_Tp>, initializer_list<_Up> __il,
| ^~~~~~~
/usr/local/include/c++/10.2.0/variant:1381:2: note: template argument deduction/substitution failed:
main.cpp:10:15: note: mismatched types 'std::in_place_type_t<_Tp>' and 'unsigned int'
10 | var_t v3{1u}; //ERROR
| ^
In file included from main.cpp:2:
/usr/local/include/c++/10.2.0/variant:1371:2: note: candidate: 'template<class _Tp, class ... _Args, class> constexpr std::variant<_Types>::variant(std::in_place_type_t<_Tp>, _Args&& ...) [with _Tp = _Tp; _Args = {_Args ...}; <template-parameter-2-3> = <template-parameter-1-3>; _Types = {long int, long unsigned int, double}]'
1371 | variant(in_place_type_t<_Tp>, _Args&&... __args)
| ^~~~~~~
/usr/local/include/c++/10.2.0/variant:1371:2: note: template argument deduction/substitution failed:
main.cpp:10:15: note: mismatched types 'std::in_place_type_t<_Tp>' and 'unsigned int'
10 | var_t v3{1u}; //ERROR
| ^
In file included from main.cpp:2:
/usr/local/include/c++/10.2.0/variant:1361:2: note: candidate: 'template<class _Tp, class, class, class _Tj, class> constexpr std::variant<_Types>::variant(_Tp&&) [with _Tp = _Tp; <template-parameter-2-2> = <template-parameter-1-2>; <template-parameter-2-3> = <template-parameter-1-3>; _Tj = _Tj; <template-parameter-2-5> = <template-parameter-1-5>; _Types = {long int, long unsigned int, double}]'
1361 | variant(_Tp&& __t)
| ^~~~~~~
/usr/local/include/c++/10.2.0/variant:1361:2: note: template argument deduction/substitution failed:
/usr/local/include/c++/10.2.0/variant: In substitution of 'template<class ... _Types> template<class _Tp, class> using __accepted_type = std::variant<_Types>::__to_type<__accepted_index<_Tp> > [with _Tp = unsigned int&&; <template-parameter-2-2> = void; _Types = {long int, long unsigned int, double}]':
/usr/local/include/c++/10.2.0/variant:1357:9: required from here
/usr/local/include/c++/10.2.0/variant:1327:8: error: no type named 'type' in 'struct std::enable_if<false, void>'
1327 | using __accepted_type = __to_type<__accepted_index<_Tp>>;
| ^~~~~~~~~~~~~~~
/usr/local/include/c++/10.2.0/variant:1349:7: note: candidate: 'constexpr std::variant<_Types>::variant(std::variant<_Types>&&) [with _Types = {long int, long unsigned int, double}]'
1349 | variant(variant&&) = default;
| ^~~~~~~
/usr/local/include/c++/10.2.0/variant:1349:15: note: no known conversion for argument 1 from 'unsigned int' to 'std::variant<long int, long unsigned int, double>&&'
1349 | variant(variant&&) = default;
| ^~~~~~~~~
/usr/local/include/c++/10.2.0/variant:1348:7: note: candidate: 'constexpr std::variant<_Types>::variant(const std::variant<_Types>&) [with _Types = {long int, long unsigned int, double}]'
1348 | variant(const variant& __rhs) = default;
| ^~~~~~~
/usr/local/include/c++/10.2.0/variant:1348:30: note: no known conversion for argument 1 from 'unsigned int' to 'const std::variant<long int, long unsigned int, double>&'
1348 | variant(const variant& __rhs) = default;
| ~~~~~~~~~~~~~~~^~~~~
/usr/local/include/c++/10.2.0/variant:1347:7: note: candidate: 'constexpr std::variant<_Types>::variant() [with _Types = {long int, long unsigned int, double}]'
1347 | variant() = default;
| ^~~~~~~
/usr/local/include/c++/10.2.0/variant:1347:7: note: candidate expects 0 arguments, 1 provided
main.cpp:10:10: warning: unused variable 'v3' [-Wunused-variable]
10 | var_t v3{1u}; //ERROR
| ^~
The final rule we landed on for variant is that we consider only alternatives where the conversion is non-narrowing, and that narrowing determination considers the type, not the value.
Among other things:
integer to floating point is always narrowing.
signed to unsigned is always narrowing.
unsigned to signed is narrowing if the signed type can't represent all possible values of the unsigned type.
So:
For v1, the source type is int, the only non-narrowing alternative is int64_t, and it is chosen.
For v2, the source type is unsigned long. If sizeof(int64_t) == sizeof(unsigned long), as is the case for many platforms, then the only non-narrowing alternative is uint64_t, and that is chosen.
For v3, the source type is unsigned int. If int is 32 bits then both int64_t and uint64_t are viable options, and neither is better than the other. So it is ambiguous.
For this kind of thing, it's a lot clearer to use in_place_type to explicitly pick which alternative you want, instead of asking the reader to do overload resolution in their head.
The answer would be platform dependent, but on my platform (64 bit linux + gcc C++) your results make perfect sense. Just note, in all below results conversion to double has lower rank than integral promotions, so it doesn't participate.
// Below works, as 1 is signed, and can only be promoted to int64_t.
variant<int64_t,uint64_t,double> v1{1};
// Below works, as 1ul is exactly unsigned long (64 bit for me), and is exact match
variant<int64_t,uint64_t,double> v2{1ul};
// Below doesn't work, as 1u is unsigned int, and can be equally promoted to int64_t and uint64_t - thus ambiguity
variant<int64_t,uint64_t,double> v3{1u}; //ERROR
// Below doesn't work, for exactly the same reason as one above.
variant<int64_t,uint64_t,double> v4{unsigned(1)}; //ERROR
}
I found an elegant solution with extending std::variant:
#include <cstdint>
#include <variant>
using namespace std;
struct var_t : variant<int64_t,uint64_t,double> {
using variant<int64_t,uint64_t,double>::variant;
constexpr var_t(unsigned u) : variant(uint64_t(u)) {}
};
int main()
{
var_t v1{1};
var_t v2{1ul};
var_t v3{1u}; // VALID
var_t v4{unsigned(1)}; // VALID
var_t v5{uint64_t(1)};
}
live example
No need to use cumbersome in_place_type. Sure it is useful but not this case.
Thanks everybody for attention