c ++:如何编写一个std :: bind-like对象来检查超细参数? [英] c++: How to write a std::bind-like object that checks for superfulous parameters?

查看:339
本文介绍了c ++:如何编写一个std :: bind-like对象来检查超细参数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据 http://en.cppreference.com/w/cpp/效用/功能/绑定 std :: bind


成员函数运算符()

Member function operator()

...

如果调用中提供的某些参数g()不是由存储在g中的任何占位符匹配的
,未使用的参数是
计算并丢弃。

If some of the arguments that are supplied in the call to g() are not matched by any placeholders stored in g, the unused arguments are evaluated and discarded.

引用示例,可​​以做:

Quoting the examples, one can do:

void f(int n1, int n2, int n3, const int& n4, int n5) {
    std::cout << n1 << ' ' << n2 << ' ' << n3 << ' ' << n4 << ' ' << n5 << '\n';
}

int main() {
    auto f1 = std::bind(f, _2, _1, 42, std::cref(n), n);
    n = 10;
    f1(1, 2, 1001); // 1 is bound by _1, 2 is bound by _2, 1001 is unused
}

如果你将 f1 作为一个带有5个参数的函数,其中3个是固定的,你不能调用 f1 具有3个参数的常识。但是,如上面的代码所示,允许这样做,因为额外的参数被默认忽略。

If you treat f1 as a function with 5 parameters where 3 are fixed, you shouldn't be able to call f1 with 3 parameters by common sense. However, as shown in above code, you are allowed to do so because the extra parameter is silently ignored.

根据为什么从bind返回的对象忽略额外的参数?,这个行为似乎存在,因为它是方便的实行??。

According to Why do objects returned from bind ignore extra arguments?, this behavior appears to exist because it is convenient to implement??.

对我来说,这是一个令人困惑的库功能,因为它混淆了函数的arity(例如在上面的例子中5 - 3 = 3) 。我想知道是否有任何实际的用例,其中这种行为实际上是有益的?

To me, this is a rather confusing library feature as it messes up the arity of functions (e.g. 5 - 3 = 3 in above example) and defeats reasoning about function calls. I was wondering if there are any practical use cases in which this kind of behavior is actually beneficial?

更重要的是,是否可以实现 std :: bind 来禁止这种行为?

More importantly, is it possible to implement a variant of std::bind to forbid this behavior? What are the possibilities and difficulties here.

感谢

推荐答案

:限制参数数量会破坏绑定器的功能,此处

Note: Restricting the number of arguments breaks a feature of binders that is illustrated here.

我的解决方案是基于计算传递的占位符确定使用的最大占位符。感谢 Xeo 指出此错误。

My solution is based on counting the passed placeholders determining the greatest placeholder used. Thanks to Xeo for pointing out this error.

#include <functional>
#include <type_traits>
#include <utility>

template<class T, class U>
constexpr auto c_max(T&& t, U&& u)
-> typename std::remove_reference<decltype( t > u ? t : u )>::type
{  return t > u ? t : u;  }

template<class...>
struct max_placeholder : std::integral_constant<int, 0> {};

template<class T, class... Rest>
struct max_placeholder<T, Rest...>
: std::integral_constant<int, c_max(std::is_placeholder<T>::value,
                                    max_placeholder<Rest...>::value)>
{};

这会给正确计算绑定器用户数量的负担。对于一些绑定的Callables如函数指针,可以推导出参数的数量(这也允许自动提供所需的占位符数量)。一旦你以任何一种方式修正了参数数量,很容易写一个存储binder的包装器,并提供一个 operator()模板,用于检查参数数量:

This lays the burden to correctly count the number on the user of the binder. For some bound Callables such as function pointers, it is possible to deduce the number of arguments (this also allows automatically supplying the required amount of placeholders). Once you have fixed the number of arguments either way, it's easy to write a wrapper that stores a binder and provides an operator() template that checks the number of arguments:

template<class T, int N>
struct strict_binder
{
    T binder;

    template<class... Args>
    auto operator()(Args&&... args)
    -> decltype( binder(std::forward<Args>(args)...) )
    {
        static_assert(sizeof...(args) == N, "wrong number of arguments");
        return binder(std::forward<Args>(args)...);
    }
};

也可能产生替换失败而不是错误。

It is also possible to produce a substitution failure instead of an error.

由于 strict_binder binder ,您可以通过部分特化:

As the strict_binder is a binder, you can express this concept via a partial specialization:

namespace std
{
    template<class T, int N>
    struct is_bind_expression< strict_binder<T, N> >
        : public true_type
    {};
}






编写一个产生 strict_binder 的函数模板。这里有一个类似于 std :: bind 的版本:


All that remains is to write a function template that produces strict_binders. Here's a version that's similar to std::bind:

template<class F, class... Args>
auto strict_bind(F&& f, Args&&... args)
-> strict_binder<
       typename std::decay<
            decltype( std::bind(std::forward<F>(f), std::forward<Args>(args)...) )
       >::type,
       max_placeholder<typename std::remove_reference<Args>::type...>::value
   >
{
    return { std::bind(std::forward<F>(f), std::forward<Args>(args)...) };
}

基本上,返回类型是

strict_binder<decltype(std::bind(f, args...)), count_placeholders<Args...>::value>

也就是说, strict_binder 类型 std :: bind

apply -like函数,在没有传递占位符时调用绑定函数:

You can also write an apply-like function that calls the bound function when no placeholders have been passed:

template<int N, class F, class... Args>
auto strict_bind_or_call(std::integral_constant<int, N>, F&& f, Args&&... args)
-> strict_binder<
       typename std::decay<
            decltype( std::bind(std::forward<F>(f), std::forward<Args>(args)...) )
       >::type,
       N
   >
{
    return { std::bind( std::forward<F>(f), std::forward<Args>(args)... ) };
}

template<class F, class... Args>
auto strict_bind_or_call(std::integral_constant<int, 0>, F&& f, Args&&... args)
-> decltype( std::bind( std::forward<F>(f), std::forward<Args>(args)... ) () )
{
    return std::bind( std::forward<F>(f), std::forward<Args>(args)... ) ();
}

template<class F, class... Args>
auto strict_bind(F&& f, Args&&... args)
-> decltype( strict_bind_or_call( std::integral_constant<int, max_placeholder<typename std::remove_reference<Args>::type...>::value>{},
                                  std::forward<F>(f), std::forward<Args>(args)... ) )
{
    using max_placeholder_here =
        max_placeholder<typename std::remove_reference<Args>::type...>;

    return strict_bind_or_call( max_placeholder_here{},
                                std::forward<F>(f), std::forward<Args>(args)... );
}

这使用标签分派来返回一个binder或调用函数的结果。
我放弃了正确的格式化,你可能想在细节命名空间中引入别名模板。

This uses tag dispatch to either return a binder or the result of calling the function. I gave up formatting this properly, you might want to introduce alias templates in a detail namespace.

注意 strict_bind_or_call 的第二个重载中的 decltype(std :: bind(..)())重现 INVOKE / bind 语义的简单方法;我不能只写 f(args ...),因为 f 可能是一个成员函数。

Note the decltype( std::bind(..) () ) in the second overload of strict_bind_or_call is a simple way to reproduce the semantics of INVOKE / bind; I can't just write f(args...) because f might be a member function.

使用示例:

#include <iostream>

void foo(int p0, int p1)
{ std::cout << "[" << p0 << ", " << p1 << "]\n"; }

int main()
{
    auto f0 = strict_bind(foo, std::placeholders::_1, 42);
    f0(1);

    strict_bind(foo, 1, 2);
}

这篇关于c ++:如何编写一个std :: bind-like对象来检查超细参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆