tl;dr
I'd like to understand what's wrong with the first code below, i.e. what the error is telling me.
MRE
I've been able to shorten the example to the following, which generates the same error as the original code below:
#include <boost/hana/functional/overload.hpp>
auto l1 = [](int){}; using L1 = decltype(l1);
auto l2 = [](double){}; using L2 = decltype(l2);
auto l3 = [](float){}; using L3 = decltype(l3);
using Ovl = boost::hana::overload_t<L1, L2, L3>
//const // uncomment to prevent error
;
Ovl ovl{l1,l2,l3};
Ovl ovl2{ovl}; // this triggers the compilation error
Original question
I'm having trouble understanding the errors I get when I try partially applying std::visit to a visitor function obtained via boost::hana::overload. (I know that I can't pass around the name of a template function, so I can't really partially apply std::visit, so I wrapped it in a generic lambda named just visit; I've not bothered doing perfect forwarding because I hardly believe it's relevant to the question.)
#include <boost/hana/functional/overload.hpp>
#include <boost/hana/functional/partial.hpp>
#include <boost/hana/functional/curry.hpp>
#include <iomanip>
#include <iostream>
#include <variant>
#include <vector>
using var_t = std::variant<int, long, double, std::string>;
int main() {
std::vector<var_t> vec = {10, 15l, 1.5, "hello"};
auto visitor = boost::hana::overload([](auto arg) { std::cout << arg << ' '; },
[](double arg) { std::cout << std::fixed << arg << ' '; },
[](const std::string& arg) { std::cout << std::quoted(arg) << ' '; }
);
auto visit = [](auto const& visitor_, auto const&... visited_){
return std::visit(visitor_, visited_...);
};
for (auto& v: vec) {
boost::hana::partial(visit, visitor)(v); // the error is essentially
boost::hana::curry<2>(visit)(visitor)(v); // the same on these 2 lines
}
std::cout << std::endl;
}
The error is (godbolt):
In file included from deleteme.cpp:1:
/usr/include/boost/hana/functional/overload.hpp: In instantiation of ‘constexpr boost::hana::overload_t<F, G>::overload_t(F_&&, G_&& ...) [with F_ = boost::hana::overload_t<main()::<lambda(auto:22)>, main()::<lambda(double)>, main()::<lambda(const string&)> >&; G_ = {}; F = main()::<lambda(auto:22)>; G = {main()::<lambda(double)>, main()::<lambda(const string&)>}]’:
/usr/include/boost/hana/detail/ebo.hpp:62:36: required from ‘constexpr _hana::ebo<K, V, true>::ebo(T&&) [with T = boost::hana::overload_t<main()::<lambda(auto:22)>, main()::<lambda(double)>, main()::<lambda(const string&)> >&; K = boost::hana::detail::bti<1>; V = boost::hana::overload_t<main()::<lambda(auto:22)>, main()::<lambda(double)>, main()::<lambda(const string&)> >]’
/usr/include/boost/hana/basic_tuple.hpp:70:65: required from ‘constexpr boost::hana::detail::basic_tuple_impl<std::integer_sequence<long unsigned int, _Idx ...>, Xn ...>::basic_tuple_impl(Yn&& ...) [with Yn = {main()::<lambda(const auto:25&, const auto:26& ...)>, boost::hana::overload_t<main()::<lambda(auto:22)>, main()::<lambda(double)>, main()::<lambda(const string&)> >&}; long unsigned int ...n = {0, 1}; Xn = {main()::<lambda(const auto:25&, const auto:26& ...)>, boost::hana::overload_t<main()::<lambda(auto:22)>, main()::<lambda(double)>, main()::<lambda(const string&)> >}]’
/usr/include/boost/hana/basic_tuple.hpp:97:44: required from ‘constexpr boost::hana::basic_tuple<Xs>::basic_tuple(Yn&& ...) [with Yn = {main()::<lambda(const auto:25&, const auto:26& ...)>, boost::hana::overload_t<main()::<lambda(auto:22)>, main()::<lambda(double)>, main()::<lambda(const string&)> >&}; Xn = {main()::<lambda(const auto:25&, const auto:26& ...)>, boost::hana::overload_t<main()::<lambda(auto:22)>, main()::<lambda(double)>, main()::<lambda(const string&)> >}]’
/usr/include/boost/hana/functional/partial.hpp:71:15: required from ‘constexpr boost::hana::partial_t<std::integer_sequence<long unsigned int, _Idx ...>, F, X ...>::partial_t(boost::hana::make_partial_t::secret, T&& ...) [with T = {main()::<lambda(const auto:25&, const auto:26& ...)>, boost::hana::overload_t<main()::<lambda(auto:22)>, main()::<lambda(double)>, main()::<lambda(const string&)> >&}; long unsigned int ...n = {0}; F = main()::<lambda(const auto:25&, const auto:26& ...)>; X = {boost::hana::overload_t<main()::<lambda(auto:22)>, main()::<lambda(double)>, main()::<lambda(const string&)> >}]’
/usr/include/boost/hana/functional/partial.hpp:61:74: required from ‘constexpr boost::hana::partial_t<std::integer_sequence<long unsigned int, __integer_pack()(sizeof ... (X ...))...>, typename boost::hana::detail::decay<Xs>::type, typename boost::hana::detail::decay<X, typename std::remove_reference<X>::type>::type ...> boost::hana::make_partial_t::operator()(F&&, X&& ...) const [with F = main()::<lambda(const auto:25&, const auto:26& ...)>; X = {boost::hana::overload_t<main()::<lambda(auto:22)>, main()::<lambda(double)>, main()::<lambda(const string&)> >&}; typename boost::hana::detail::decay<Xs>::type = main()::<lambda(const auto:25&, const auto:26& ...)>; typename std::remove_reference<_Tp>::type = main()::<lambda(const auto:25&, const auto:26& ...)>]’
/usr/include/boost/hana/functional/curry.hpp:149:24: required from ‘constexpr decltype(auto) boost::hana::curry_t<n, F>::operator()(X&& ...) && [with X = {boost::hana::overload_t<main()::<lambda(auto:22)>, main()::<lambda(double)>, main()::<lambda(const string&)> >&}; long unsigned int n = 2; F = main()::<lambda(const auto:25&, const auto:26& ...)>]’
deleteme.cpp:31:37: required from here
/usr/include/boost/hana/functional/overload.hpp:53:61: error: no matching function for call to ‘boost::hana::overload_t<main()::<lambda(double)>, main()::<lambda(const string&)> >::overload_t()’
53 | , overload_t<G...>::type(static_cast<G_&&>(g)...)
| ^
/usr/include/boost/hana/functional/overload.hpp:51:28: note: candidate: ‘template<class F_, class ... G_> constexpr boost::hana::overload_t<F, G>::overload_t(F_&&, G_&& ...) [with F_ = F_; G_ = {G_ ...}; F = main()::<lambda(double)>; G = {main()::<lambda(const string&)>}]’
51 | constexpr explicit overload_t(F_&& f, G_&& ...g)
| ^~~~~~~~~~
/usr/include/boost/hana/functional/overload.hpp:51:28: note: template argument deduction/substitution failed:
/usr/include/boost/hana/functional/overload.hpp:53:61: note: candidate expects at least 1 argument, 0 provided
53 | , overload_t<G...>::type(static_cast<G_&&>(g)...)
| ^
/usr/include/boost/hana/functional/overload.hpp:42:12: note: candidate: ‘constexpr boost::hana::overload_t<main()::<lambda(double)>, main()::<lambda(const string&)> >::overload_t(const boost::hana::overload_t<main()::<lambda(double)>, main()::<lambda(const string&)> >&)’
42 | struct overload_t
| ^~~~~~~~~~
/usr/include/boost/hana/functional/overload.hpp:42:12: note: candidate expects 1 argument, 0 provided
/usr/include/boost/hana/functional/overload.hpp:42:12: note: candidate: ‘constexpr boost::hana::overload_t<main()::<lambda(double)>, main()::<lambda(const string&)> >::overload_t(boost::hana::overload_t<main()::<lambda(double)>, main()::<lambda(const string&)> >&&)’
/usr/include/boost/hana/functional/overload.hpp:42:12: note: candidate expects 1 argument, 0 provided
However, if I define visit to be "manually" curried, it behaves as I expect (godbolt):
#include <boost/hana/functional/overload.hpp>
#include <boost/hana/functional/partial.hpp>
#include <boost/hana/functional/curry.hpp>
#include <iomanip>
#include <iostream>
#include <variant>
#include <vector>
// the variant to visit
using var_t = std::variant<int, long, double, std::string>;
int main() {
std::vector<var_t> vec = {10, 15l, 1.5, "hello"};
auto visitor = boost::hana::overload([](auto arg) { std::cout << arg << ' '; },
[](double arg) { std::cout << std::fixed << arg << ' '; },
[](const std::string& arg) { std::cout << std::quoted(arg) << ' '; }
);
auto visit_c = [](auto const& visitor_){
return [&visitor_](auto const&... visited_){
return std::visit(visitor_, visited_...);
};
};
for (auto& v: vec) {
visit_c(visitor)(v);
}
std::cout << std::endl;
}
(Example adapted from cppreference.)
On top of the above MRE, I've verified that these assertions all pass,
which, given how
boost::hana::ovearload_tis written, means that the instantiation ofboost::hana::overload_t<L1, L2, L3>has this form (not valid C++, here I play the role of the compiler):Therefore, when the compiler sees
Ovl ovl2{ovl};, it has to constructovl2fromovl; to do so, there are two possibilities:template <typename F_, typename ...G_> constexpr explicit overload_t<L1, L2, L3>::overload_t(F_&& f, G_&& ...g)withF_ = overload_t<L1, L2, L3>&and an empty packG_..., which results in a constructor with signatureoverload_t<L1, L2, L3>::overload_t(overload_t<L1, L2, L3>&)which is good for copy a non-constlvalue (such asovl);The implicitly defined copy constructor, however, has signature
overload_t<L1, L2, L3>::overload_t(overload_t<L1, L2, L3> const&), which is not as good a match asoverload_t<L1, L2, L3>::overload_t(overload_t<L1, L2, L3>&)when they are fed with the non-constlvalueovl, so the compliler will have to choose the first alternative above, which will in turn make it try the constructionoverload_t<L2, L3>(), which maps nicely to the error:Making
ovlconstsolves the problem because it makes the implicitly defined copy constructor,overload_t<L1, L2, L3>::overload_t(overload_t<L1, L2, L3> const&)an exact match; the template constructor too is an exact match when instantiated withF_ = overload_t<L1, L2, L3> const&, but the former is preferred because it is not templated.std::moveingovltoo solves the problem, for the same reason: the implicitly defined copy constructor is an exact match for copying an rvalue, and it is preferred to the likewise exact match of the templated constructor.