完美的转发功能包装器?
Perfectly forwarding function wrapper?
我们考虑以下仿函数,以帮助检测其 operator()
的哪个版本被调用。
struct functor
{
void operator()() {std::cout << "functor::operator" << std::endl;}
void operator()() const {std::cout << "functor::operator const" << std::endl;}
void operator()() volatile {std::cout << "functor::operator volatile" << std::endl;}
void operator()() const volatile {std::cout << "functor::operator const volatile" << std::endl;}
void operator()(int) & {std::cout << "functor::operator &" << std::endl;}
void operator()(int) const& {std::cout << "functor::operator const&" << std::endl;}
void operator()(int) volatile& {std::cout << "functor::operator volatile&" << std::endl;}
void operator()(int) const volatile& {std::cout << "functor::operator const volatile&" << std::endl;}
void operator()(int) && {std::cout << "functor::operator &&" << std::endl;}
void operator()(int) const&& {std::cout << "functor::operator const&&" << std::endl;}
void operator()(int) volatile&& {std::cout << "functor::operator volatile&&" << std::endl;}
void operator()(int) const volatile&& {std::cout << "const volatile&&" << std::endl;}
};
我想知道,在 C++17
中(当然如果可行的话)如何编写两个仿函数包装器,将函数存储在元组中并使用 class template argument deduction:
- 第一个
wrapper1
将根据 函子 的限定符调用运算符
- 第二个
wrapper2
将根据 wrapper 的限定符调用运算符
例如:
wrapper1 w1(functor{});
wrapper2 w2(functor{});
w1(0); // should call the && version since the functor was built as a temporary
w2(0); // should call the & version since the wrapper was built as a value
这里是对我正在寻找的东西的一些非常粗略的探索。注意:这只是为了说明我的想法 "taste"。
template <class F>
struct wrapper_a
{
constexpr wrapper_a(F f) noexcept: _tuple(f) {}
template <class... Args>
void operator()(Args&&... args)
{std::get<0>(_tuple)(std::forward<Args>(args)...);}
std::tuple<F> _tuple;
};
template <class F>
struct wrapper_b
{
template <class G>
constexpr wrapper_b(G&& g) noexcept: _tuple(std::forward<G>(g)) {}
template <class... Args>
void operator()(Args&&... args)
{std::get<0>(_tuple)(std::forward<Args>(args)...);}
std::tuple<F> _tuple;
};
template <class F> wrapper_b(F&& f) -> wrapper_b<F>;
template <class F>
struct wrapper_c
{
template <class G>
constexpr wrapper_c(G&& g) noexcept: _tuple(std::forward<G>(g)) {}
template <class... Args>
void operator()(Args&&... args)
{std::forward<F>(std::get<0>(_tuple))(std::forward<Args>(args)...);}
std::tuple<F> _tuple;
};
template <class F> wrapper_c(F&& f) -> wrapper_c<F>;
如何实现我想要的?这可能吗?
不确定是否了解您的确切要求...而且我是参考转发的新手...无论如何
The first one wrapper1 that will call the operator depending on the qualifiers of the functor
也许专家可以避免这种复杂情况,但在我看来你需要两个模板参数
template <typename F, typename G>
struct wrapper1
其中 F
是传递给构造函数的参数副本的类型,G
是预期类型。
所以你有一个 F
值
F f;
您可以通过转发使用它
template <typename ... Args>
void operator() (Args && ... as) &
{ std::forward<G>(f)(std::forward<Args>(as)...); }
为了简化,您可以定义一个模板推导如下
template <typename F>
wrapper1(F && f) -> wrapper1<std::decay_t<F>, decltype(std::forward<F>(f))>;
The second one wrapper2 that will call the operator depending on the qualifiers of the wrapper
也许我错了,但在我看来这更简单一些。
您只需要一个模板参数
template <typename F>
struct wrapper2
并且您只需在 r-reference 运算符中使用 std::move()
template <typename ... Args>
void operator() (Args && ... as) &&
{ std::move(f)(std::forward<Args>(as)...); }
推演指南简单
template <typename F>
wrapper2(F && f) -> wrapper2<std::decay_t<F>>;
以下是有限的(const
/非const
和 l-value/r-value 参考替代方案)但完整的工作示例。
观察到有一些常量问题引起的编译错误(如果你用一个not-const
callable初始化一个const wrapper1
,就会有问题)
#include <iostream>
struct functor
{
void operator()(int) &
{std::cout << "functor::operator &" << std::endl;}
void operator()(int) const &
{std::cout << "functor::operator const &" << std::endl;}
void operator()(int) &&
{std::cout << "functor::operator &&" << std::endl;}
void operator()(int) const &&
{std::cout << "functor::operator const &&" << std::endl;}
};
template <typename F, typename G>
struct wrapper1
{
template <typename H>
constexpr wrapper1 (H && f0) noexcept : f{std::forward<H>(f0)}
{}
template <typename ... Args>
void operator() (Args && ... as) &
{ std::forward<G>(f)(std::forward<Args>(as)...); }
template <typename ... Args>
void operator() (Args && ... as) const &
{ std::forward<G>(f)(std::forward<Args>(as)...); }
template <typename ... Args>
void operator() (Args && ... as) &&
{ std::forward<G>(f)(std::forward<Args>(as)...); }
template <typename ... Args>
void operator() (Args && ... as) const &&
{ std::forward<G>(f)(std::forward<Args>(as)...); }
F f;
};
template <typename F>
wrapper1(F && f)
-> wrapper1<std::decay_t<F>, decltype(std::forward<F>(f))>;
template <typename F>
struct wrapper2
{
template <typename H>
constexpr wrapper2 (H && f0) noexcept : f{std::forward<H>(f0)}
{}
template <typename ... Args>
void operator() (Args && ... as) &
{ f(std::forward<Args>(as)...); }
template <typename ... Args>
void operator() (Args && ... as) const &
{ f(std::forward<Args>(as)...); }
template <typename ... Args>
void operator() (Args && ... as) &&
{ std::move(f)(std::forward<Args>(as)...); }
template <typename ... Args>
void operator() (Args && ... as) const &&
{ std::move(f)(std::forward<Args>(as)...); }
F f;
};
template <typename F>
wrapper2(F && f) -> wrapper2<std::decay_t<F>>;
int main ()
{
functor fc;
functor const fd;
wrapper1 w1a{functor{}};
wrapper1 w1b{static_cast<functor const &&>(functor{})};
wrapper1 w1c{fc};
wrapper1 w1d{fd};
wrapper1 const w1e{functor{}};
wrapper1 const w1f{static_cast<functor const &&>(functor{})};
wrapper1 const w1g{fc};
wrapper1 const w1h{fd};
w1a(0);
w1b(0);
w1c(0);
w1d(0);
std::cout << "----------------------------" << std::endl;
// w1e(0); // compilation error
w1f(0);
// w1g(0); // compilation error
w1h(0);
std::cout << "----------------------------" << std::endl;
wrapper1<functor, functor&&>{functor{}}(0);
wrapper1<functor, functor const &&>
{static_cast<functor const &&>(functor{})}(0);
wrapper1<functor, functor&>{fc}(0);
wrapper1<functor, functor const &>{fd}(0);
std::cout << "----------------------------" << std::endl;
// (wrapper1 <functor, functor&&> const)
//{functor{}}(0); // compilation error
(wrapper1<functor, functor const &&> const)
{static_cast<functor const &&>(functor{})}(0);
// (wrapper1<functor, functor&> const){fc}(0); // compilation error
(wrapper1<functor, functor const &> const){fd}(0);
wrapper2 w2a{functor{}};
wrapper2 w2b{static_cast<functor const &&>(functor{})};
wrapper2 w2c{fc};
wrapper2 w2d{fd};
wrapper2 const w2e{functor{}};
wrapper2 const w2f{static_cast<functor const &&>(functor{})};
wrapper2 const w2g{fc};
wrapper2 const w2h{fd};
std::cout << "----------------------------" << std::endl;
w2a(0);
w2b(0);
w2c(0);
w2d(0);
std::cout << "----------------------------" << std::endl;
w2e(0);
w2f(0);
w2g(0);
w2h(0);
std::cout << "----------------------------" << std::endl;
wrapper2<functor>{functor{}}(0);
wrapper2<functor>{static_cast<functor const &&>(functor{})}(0);
wrapper2<functor>{fc}(0);
wrapper2<functor>{fd}(0);
std::cout << "----------------------------" << std::endl;
(wrapper2<functor> const){functor{}}(0);
(wrapper2<functor> const){static_cast<functor const &&>(functor{})}(0);
(wrapper2<functor> const){fc}(0);
(wrapper2<functor> const){fd}(0);
}
我们考虑以下仿函数,以帮助检测其 operator()
的哪个版本被调用。
struct functor
{
void operator()() {std::cout << "functor::operator" << std::endl;}
void operator()() const {std::cout << "functor::operator const" << std::endl;}
void operator()() volatile {std::cout << "functor::operator volatile" << std::endl;}
void operator()() const volatile {std::cout << "functor::operator const volatile" << std::endl;}
void operator()(int) & {std::cout << "functor::operator &" << std::endl;}
void operator()(int) const& {std::cout << "functor::operator const&" << std::endl;}
void operator()(int) volatile& {std::cout << "functor::operator volatile&" << std::endl;}
void operator()(int) const volatile& {std::cout << "functor::operator const volatile&" << std::endl;}
void operator()(int) && {std::cout << "functor::operator &&" << std::endl;}
void operator()(int) const&& {std::cout << "functor::operator const&&" << std::endl;}
void operator()(int) volatile&& {std::cout << "functor::operator volatile&&" << std::endl;}
void operator()(int) const volatile&& {std::cout << "const volatile&&" << std::endl;}
};
我想知道,在 C++17
中(当然如果可行的话)如何编写两个仿函数包装器,将函数存储在元组中并使用 class template argument deduction:
- 第一个
wrapper1
将根据 函子 的限定符调用运算符
- 第二个
wrapper2
将根据 wrapper 的限定符调用运算符
例如:
wrapper1 w1(functor{});
wrapper2 w2(functor{});
w1(0); // should call the && version since the functor was built as a temporary
w2(0); // should call the & version since the wrapper was built as a value
这里是对我正在寻找的东西的一些非常粗略的探索。注意:这只是为了说明我的想法 "taste"。
template <class F>
struct wrapper_a
{
constexpr wrapper_a(F f) noexcept: _tuple(f) {}
template <class... Args>
void operator()(Args&&... args)
{std::get<0>(_tuple)(std::forward<Args>(args)...);}
std::tuple<F> _tuple;
};
template <class F>
struct wrapper_b
{
template <class G>
constexpr wrapper_b(G&& g) noexcept: _tuple(std::forward<G>(g)) {}
template <class... Args>
void operator()(Args&&... args)
{std::get<0>(_tuple)(std::forward<Args>(args)...);}
std::tuple<F> _tuple;
};
template <class F> wrapper_b(F&& f) -> wrapper_b<F>;
template <class F>
struct wrapper_c
{
template <class G>
constexpr wrapper_c(G&& g) noexcept: _tuple(std::forward<G>(g)) {}
template <class... Args>
void operator()(Args&&... args)
{std::forward<F>(std::get<0>(_tuple))(std::forward<Args>(args)...);}
std::tuple<F> _tuple;
};
template <class F> wrapper_c(F&& f) -> wrapper_c<F>;
如何实现我想要的?这可能吗?
不确定是否了解您的确切要求...而且我是参考转发的新手...无论如何
The first one wrapper1 that will call the operator depending on the qualifiers of the functor
也许专家可以避免这种复杂情况,但在我看来你需要两个模板参数
template <typename F, typename G>
struct wrapper1
其中 F
是传递给构造函数的参数副本的类型,G
是预期类型。
所以你有一个 F
值
F f;
您可以通过转发使用它
template <typename ... Args>
void operator() (Args && ... as) &
{ std::forward<G>(f)(std::forward<Args>(as)...); }
为了简化,您可以定义一个模板推导如下
template <typename F>
wrapper1(F && f) -> wrapper1<std::decay_t<F>, decltype(std::forward<F>(f))>;
The second one wrapper2 that will call the operator depending on the qualifiers of the wrapper
也许我错了,但在我看来这更简单一些。
您只需要一个模板参数
template <typename F>
struct wrapper2
并且您只需在 r-reference 运算符中使用 std::move()
template <typename ... Args>
void operator() (Args && ... as) &&
{ std::move(f)(std::forward<Args>(as)...); }
推演指南简单
template <typename F>
wrapper2(F && f) -> wrapper2<std::decay_t<F>>;
以下是有限的(const
/非const
和 l-value/r-value 参考替代方案)但完整的工作示例。
观察到有一些常量问题引起的编译错误(如果你用一个not-const
callable初始化一个const wrapper1
,就会有问题)
#include <iostream>
struct functor
{
void operator()(int) &
{std::cout << "functor::operator &" << std::endl;}
void operator()(int) const &
{std::cout << "functor::operator const &" << std::endl;}
void operator()(int) &&
{std::cout << "functor::operator &&" << std::endl;}
void operator()(int) const &&
{std::cout << "functor::operator const &&" << std::endl;}
};
template <typename F, typename G>
struct wrapper1
{
template <typename H>
constexpr wrapper1 (H && f0) noexcept : f{std::forward<H>(f0)}
{}
template <typename ... Args>
void operator() (Args && ... as) &
{ std::forward<G>(f)(std::forward<Args>(as)...); }
template <typename ... Args>
void operator() (Args && ... as) const &
{ std::forward<G>(f)(std::forward<Args>(as)...); }
template <typename ... Args>
void operator() (Args && ... as) &&
{ std::forward<G>(f)(std::forward<Args>(as)...); }
template <typename ... Args>
void operator() (Args && ... as) const &&
{ std::forward<G>(f)(std::forward<Args>(as)...); }
F f;
};
template <typename F>
wrapper1(F && f)
-> wrapper1<std::decay_t<F>, decltype(std::forward<F>(f))>;
template <typename F>
struct wrapper2
{
template <typename H>
constexpr wrapper2 (H && f0) noexcept : f{std::forward<H>(f0)}
{}
template <typename ... Args>
void operator() (Args && ... as) &
{ f(std::forward<Args>(as)...); }
template <typename ... Args>
void operator() (Args && ... as) const &
{ f(std::forward<Args>(as)...); }
template <typename ... Args>
void operator() (Args && ... as) &&
{ std::move(f)(std::forward<Args>(as)...); }
template <typename ... Args>
void operator() (Args && ... as) const &&
{ std::move(f)(std::forward<Args>(as)...); }
F f;
};
template <typename F>
wrapper2(F && f) -> wrapper2<std::decay_t<F>>;
int main ()
{
functor fc;
functor const fd;
wrapper1 w1a{functor{}};
wrapper1 w1b{static_cast<functor const &&>(functor{})};
wrapper1 w1c{fc};
wrapper1 w1d{fd};
wrapper1 const w1e{functor{}};
wrapper1 const w1f{static_cast<functor const &&>(functor{})};
wrapper1 const w1g{fc};
wrapper1 const w1h{fd};
w1a(0);
w1b(0);
w1c(0);
w1d(0);
std::cout << "----------------------------" << std::endl;
// w1e(0); // compilation error
w1f(0);
// w1g(0); // compilation error
w1h(0);
std::cout << "----------------------------" << std::endl;
wrapper1<functor, functor&&>{functor{}}(0);
wrapper1<functor, functor const &&>
{static_cast<functor const &&>(functor{})}(0);
wrapper1<functor, functor&>{fc}(0);
wrapper1<functor, functor const &>{fd}(0);
std::cout << "----------------------------" << std::endl;
// (wrapper1 <functor, functor&&> const)
//{functor{}}(0); // compilation error
(wrapper1<functor, functor const &&> const)
{static_cast<functor const &&>(functor{})}(0);
// (wrapper1<functor, functor&> const){fc}(0); // compilation error
(wrapper1<functor, functor const &> const){fd}(0);
wrapper2 w2a{functor{}};
wrapper2 w2b{static_cast<functor const &&>(functor{})};
wrapper2 w2c{fc};
wrapper2 w2d{fd};
wrapper2 const w2e{functor{}};
wrapper2 const w2f{static_cast<functor const &&>(functor{})};
wrapper2 const w2g{fc};
wrapper2 const w2h{fd};
std::cout << "----------------------------" << std::endl;
w2a(0);
w2b(0);
w2c(0);
w2d(0);
std::cout << "----------------------------" << std::endl;
w2e(0);
w2f(0);
w2g(0);
w2h(0);
std::cout << "----------------------------" << std::endl;
wrapper2<functor>{functor{}}(0);
wrapper2<functor>{static_cast<functor const &&>(functor{})}(0);
wrapper2<functor>{fc}(0);
wrapper2<functor>{fd}(0);
std::cout << "----------------------------" << std::endl;
(wrapper2<functor> const){functor{}}(0);
(wrapper2<functor> const){static_cast<functor const &&>(functor{})}(0);
(wrapper2<functor> const){fc}(0);
(wrapper2<functor> const){fd}(0);
}