c++ - Calling const mutable lambdas -
to simplify testcase, suppose have following wrapper class:
template <typename t> struct wrapper { decltype(auto) operator()() const { return m_t(); } decltype(auto) operator()() { return m_t(); } t m_t; }; template <typename t> auto make_wrapper(t t) { return wrapper<t>{t}; }
and let’s wrapping following trivial functor returning references:
struct foo { int& operator()() { return x; } const int& operator()() const { return x; } int x; };
in main
function, trying wrap foo
functor lambda closure. since want return non-const references, setting mutable
, using decltype(auto)
:
int main() { foo foo; auto fun = [foo]() mutable -> decltype(auto) { return foo(); }; auto wfun = make_wrapper(fun); const auto& cwfun = wfun; wfun(); // <- ok cwfun(); // <- bad! }
for second call, cwfun()
, first const
version of wrapper::operator()
called, there m_t
viewed const
lambda, , cannot called. suppose because m_t
marked mutable
in first place. way of making work? convert m_t
non-const
before calling in operator() const
?
goals
my goal call cwfun()
call wrapper::operator() const
, foo::operator() const
. mark wrapper::m_t
mutable
fix compiler error, foo::operator()
called instead of foo::operator() const
.
alternatively, can add const
in wrapper::operator() const
since know foo::operator()
, foo::operator() const
differ constness. using like:
return const_cast<typename std::add_lvalue_reference<typename std::add_const<typename std::remove_reference<decltype(m_t())>::type>::type>::type>(m_t());
but yes, that’s heavy.
errors , coliru paste
the error message given clang looks like:
tc-refptr.cc:8:12: error: no matching function call object of type 'const (lambda @ tc-refptr.cc:40:14)' return m_t(); ^~~ tc-refptr.cc:44:27: note: in instantiation of member function 'wrapper<(lambda @ tc-refptr.cc:40:14)>::operator()' requested here debugtype<decltype(cwfun())> df; ^ tc-refptr.cc:40:14: note: candidate function not viable: 'this' argument has type 'const (lambda @ tc-refptr.cc:40:14)', method not marked const auto fun = [foo]() mutable -> decltype(auto) { return foo(); };
first start partial_apply
, in case written const-sensitive:
template<class f, class...args> struct partial_apply_t { std::tuple<args...> args; f f; template<size_t...is, class self, class...extra> static auto apply( self&& self, std::index_sequence<is...>, extra&&...extra ) -> decltype( (std::forward<self>(self).f)( std::get<is>(std::forward<self>(self).args)..., std::declval<extra>()... ) { return std::forward<self>(self).f( std::get<is>(std::forward<self>(self).args)..., std::forward<extra>(extra)... ); } partial_apply_t(partial_apply_t const&)=default; partial_apply_t(partial_apply_t&&)=default; partial_apply_t& operator=(partial_apply_t const&)=default; partial_apply_t& operator=(partial_apply_t&&)=default; ~partial_apply_t()=default; template<class f0, class...us, class=std::enable_if_t< std::is_convertible<std::tuple<f0, us...>, std::tuple<f, args...>>{} > > partial_apply_t(f0&& f0, us&&...us): f(std::forward<f0>(f0)), args(std::forward<us>(us)...) {} // 3 operator() overloads. more, lazy: template<class...extra, class indexes=std::index_sequence_for<extra>> auto operator()(extra&&...extra)const& -> decltype( apply( std::declval<partial_apply_t const&>(), indexes{}, std::declval<extra>()... ) ) { return apply( *this, indexes{}, std::forward<extra>(extra)... ); } template<class...extra, class indexes=std::index_sequence_for<extra>> auto operator()(extra&&...extra)& -> decltype( apply( std::declval<partial_apply_t&>(), indexes{}, std::declval<extra>()... ) ) { return apply( *this, indexes{}, std::forward<extra>(extra)... ); } template<class...extra, class indexes=std::index_sequence_for<extra>> auto operator()(extra&&...extra)&& -> decltype( apply( std::declval<partial_apply_t&&>(), indexes{}, std::declval<extra>()... ) ) { return apply( std::move(*this), indexes{}, std::forward<extra>(extra)... ); } }; template<class f, class... ts> partial_apply_t<std::decay_t<f>, std::decay_t<ts>...> partial_apply(f&& f, ts&&...ts) { return {std::forward<f>(f), std::forward<ts>(ts)...}; }
then use it:
auto fun = partial_apply( [](auto&& foo) -> decltype(auto) { return foo(); }, foo );
now copy of foo
stored in partial_apply
, , @ point invoke it passed (in correct const-correctness) lambda. lambda gets different const-ness of foo
depending on call context of fun
.
other fact have typo above, other thing should handle std::ref
, like, when expands args
converts std::reference_wrapper
references.
that shouldn't hard: reference_unwrapper
passes non-reference-wrapped things through, , unwraps std::reference_wrapper
s.
alternatively, unwrap in partial_apply
function, instead of decay_t
ing.
Comments
Post a Comment