std :: function与std :: bind完美结合-但是为什么呢?

时间:2019-07-06 18:59:47

标签: c++ c++11 c++17 std-function stdbind

我正在使用$status = $_POST['status'] ?? "0"; if($status = "1") { header("location:enroll.php"); //when system is on exit; } else { header("location:close.php"); //when system is off exit; } 生成素数std::uniform_int_distribution。我将分发对象放在一个匿名的命名空间中-对于成年人来说,这似乎是C ++的“静态链接” ...

(p)

请注意,我在可移植代码允许的范围内尽可能深入地播种了Mersenne Twister。不过,这对于这个问题并不是很重要。只是为了向读者保证我正确使用了随机工具:

namespace
{
    // a more pedantic range: [2, 18446744073709551557]
    std::uniform_int_distribution<uint64_t> p_dist {2};

    std::mt19937 rng; // (sufficient IV states for uniqueness)
}

这非常方便,因为我具有使用(7) bases的确定性std::seed_seq::result_type data[rng.state_size]; std::random_device rdev; std::generate_n(data, rng.state_size, std::ref(rdev)); std::seed_seq rng_seed (data, data + rng.state_size); rng.seed(rng_seed); 函数,可以确定u64_prime(p)是否为素数:

(p)

现在,我创建一个uint64_t p; while (!u64_prime(p = p_dist(rng))) ; 对象:

std::function

此函数:std::function<uint64_t()> zp_rng = std::bind( decltype(p_dist){0, p - 1}, std::ref(rng)); 可以被调用以返回zp_rng()中的随机数。也就是说,将分发对象用于:Z(p)来自引用的 rng 的结果。


现在,这非常令人印象深刻-但我已经通过剪切粘贴有效地采用了它,而对[0, p - 1]之间的相互作用以及为{ {1}}。

我对std::function并不感到困惑-这只是一种指定我们仍要使用std::bind的方式。我对decltype(p_dist){0, p - 1}的理解是,它防止实例化 rng 的本地副本,并强制使用引用代替...所以:


Q :有效确定以下各项的基本规则是什么:std::uniform_int_distribution被使用-我不明白为什么std::ref(rng)会强制执行此交互。许多交互似乎基于dist(rng)方法。

Q std::bindcppreference.com上被称为“通用多态函数包装器”。那么,它是封装了operator ()返回类型的函数吗?还是再次利用std::function语法来驱动函数的概念?

尽管这些构造非常有用,但我觉得我在某种程度上是一种狂热的编程。我正在寻找一个能以具体方式解决任何歧义并为类似问题提供见解的答案-uint64_t自变量如何相互作用,以及operator ()签名如何反映这一点?


我没有收到关于使用bind的任何积极反馈。即使在这种简单情况下,也可以通过简单地使用lambda函数获得出色的结果(和代码生成)。我自己的测试验证了这一点。

4 个答案:

答案 0 :(得分:6)

std::bindstd::function无关,它所做的只是包装可调用的内容,以便始终传递一组特定的参数,请参见https://en.cppreference.com/w/cpp/utility/functional/bind

std::function仅接受适合您指定为其模板参数的函数签名的任何可调用对象。

std::uniform_int_distribution是可调用的,因为它指定了operator(),请参见https://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution/operator()。具体来说就是生成器。我们使用bind围绕它创建包装器,这样我们就不必总是显式地传递生成器。然后将结果存储在与uint64_t返回类型匹配且不带参数的std::function中(因为绑定了生成器参数)。

如果您不熟悉此概念,则应阅读有关运算符重载的信息,请参阅https://en.cppreference.com/w/cpp/language/operators(特别是函数调用运算符)。

答案 1 :(得分:3)

在现代C ++中无需使用std::bind-改用lambda:

// NOTE: p and rng are captured by reference and must outlive zp_rng!
std::function<uint64_t()> zp_rng = [&]() { return decltype(p_dist){0, p - 1}(rng); };

对于std::bindstd::function实例,它们是可调用的函数对象,即它们具有operator()

答案 2 :(得分:3)

  

Q:什么是有效确定的基本规则:dist(rng)被使用-我不明白为什么std :: bind将强制执行此交互。许多交互似乎基于operator()方法。

std::bind执行function composition。第一个参数必须是一个函数对象,即callable之类的函数(例如普通函数或带有重载operator()的类)。

std::bind的调用将创建其参数的副本,将参数的副本“绑定”到第一个参数(功能对象),然后返回一个新的功能对象,该对象将调用该功能对象的副本

所以在一个简单的情况下:

int f(int i) { return i; }
auto f1 = std::bind(f, 1);

这会将值1绑定到函数f,从而创建了一个新的函数对象,该对象可以不带参数地调用。当您调用f1()时,它将使用参数f调用1,即它将调用f(1),并返回返回的任何内容(在本例中为{{1 }}。

1返回的事物的实际类型是特定于实现的类类型,可能称为std::bind(f, 1)之类。您并不是要直接引用该类型,要么使用std::__detail::__bind_type<void(*)(int), int>捕获对象,要么将其存储在其他与精确类型无关的东西中,所以要么:

auto

或:

auto f1 = std::bind(f, 1);

在更复杂的情况下,调用std::function<int()> f1 = std::bind(f, 1); 时会得到一个新的函数对象,该对象包含一个临时std::bind(decltype(p_dist){0, p - 1}, std::ref(rng)))的副本和一个{{1 }}。当您调用该新功能对象时,它将调用包含的分布,并将其传递给decltype(p_dist){0, p - 1}的引用。

这意味着它可以不带任何参数地调用,并且它将使用包含的随机引擎调用包含的随机数分布,并返回结果。

  

Q: reference_wrapper<std::mt19937>在cppreference.com上被称为“通用多态函数包装器”。那么它是封装std::ref(rng)返回类型的函数吗?

rng是一个函数对象的包装,该函数对象可无参数调用并且返回std::function(或可隐式转换为uint64_t的对象)。它可以用来存储任意函数对象的副本,该对象是可复制构造的,并且可以无参数调用,并返回可转换为std::function<uint64_t()>的对象。

(通常,uint64_t是函数对象的包装,当使用N个参数调用uint64_tuint64_t类型的函数时,该对象返回std::function<R(A1, A2 ... AN)> ... R。)

由于A1调用的结果是可复制构造的函数对象,该对象可无参数调用并返回A2,因此可以将AN调用的结果存储在std::bind,当您调用uint64_t时,它将调用std::bind调用的结果,该调用将调用所包含的分发,并返回结果。

  

还是再次利用operator()语法来驱动函数的概念?

我不确定这是什么意思,但是总的来说,在C ++中我们经常谈论“函数对象”或“可调用对象”,它们是函数的泛化,即可以使用函数调用语法调用的东西,例如std::function<uint64_t()>

答案 3 :(得分:0)

我引用的是A Tour of C++ 什么是bind()

  

函数适配器将一个函数作为参数并返回一个函数   可用于调用原始函数的对象。标准   库提供了bind()mem_fn()适配器来做参数   绑定,也称为 Currying 部分评估。粘合剂是   过去大量使用,但大多数使用似乎更容易   用lambda表示。

double cube(double);
auto cube2 = bind(cube,2);
     

调用cube2()将使用参数cube调用2,即,   cube(2)

     

bind()可以直接使用,也可以用于初始化   自动变量。 bind()类似于lambda。

     

如果我们想将bind()的结果分配给带有   特定类型,我们可以使用标准库类型function。一种   function指定了特定的返回类型和特定的   参数类型。

     

标准库function是一种类型,可以容纳您所需要的任何对象   可以使用 call运算符 ()进行调用。也就是说,一个类型的对象   function function object

因此std::function是一个函数对象。 功能对象用于定义可以像函数一样调用的对象。 这是函数对象的示例:

template<typename T>
class Cube2 {
const T val; // value to compare against
public:
Cube2 (const T& v) :val(v) { }
double operator()(const T& x) const { return pow(x,3); } // call operator
};

auto cube2 = bind(cube,2);就像说Cube2<int> cube2{2};