C++ std::mem_fn 和 std::bind 相反
C++ std::mem_fn and std::bind the other way around
所以我一直在研究使用抽象算法在我的代码中重用重复出现的模式。具体来说,我想确定节点数组中具有最高 'score' 的元素,这是通过评估复杂的评分成员函数来确定的。
在 之后我想出了 (C++17)
template <typename FwdIt, typename Eval, typename Pred = std::less<>>
constexpr FwdIt max_eval_element(FwdIt first, FwdIt last, Eval eval, Pred pred = Pred()) {
FwdIt found = first;
if (first != last) {
auto best = eval(*found);
while (++first != last) {
if (auto const thisVal = eval(*first);
pred(best, thisVal)) {
found = first;
best = thisVal;
}
}
}
return found;
}
所以考虑我的节点 class:
class Node {
private:
double val;
public:
Node(double val) noexcept : val(val) {}
[[nodiscard]] auto Score1() const noexcept {
return std::sqrt(std::log(10.0 / val));
}
[[nodiscard]] auto Score2(double other) const noexcept {
return std::sqrt(std::log(other / val));
}
};
和我的节点数组:
std::array<Node, 100000> nodes;
我可以打电话
auto const& Node = *std::max_eval_element(std::cbegin(nodes), std::cend(nodes), std::mem_fn(&Node::Score1));
但现在我想为 Score2
重复此操作,其中输入取决于一些局部变量...当然我可以编写一些 lambda 函数...但我们有 std::bind
那个,对吧?我知道你可以在
这样的成员函数上调用绑定
std::bind(this, std::mem_fn(Node::Score1));
但我想要的是反过来。哪个不行。
auto const& Node = *std::max_eval_element(std::cbegin(nodes), std::cend(nodes), std::bind(std::mem_fn(&Node::Score2), 1.0));
我换了个方法试了,还是不行
auto const& Node = *std::max_eval_element(std::cbegin(nodes), std::cend(nodes), std::mem_fn(std::bind(&Node::Score2), 1.0));
我知道为什么这不起作用...成员函数需要对象指针作为(隐藏的)第一个参数。但这将意味着我们缺少类似 std::bind_mem_fn
左右的东西...我们过去有 std::bind2nd
,但它已在 C++17 中删除...
再说一次:我当然可以使用 lambda,但考虑到 std:mem_fn
和 std::bind
之类的东西存在,抽象算法是一件好事...
我是不是遗漏了什么,或者这只是标准中遗漏的?
呼叫std::bind(&Node::Score2)
是问题所在。它缺少传递给 Score2
的参数。你想要:
std::bind(&Node::Score2, std::placeholders::_1, 1.0)
这不是指向成员的指针,因此它不是 std::mem_fn
的适当参数
或者,您可以使用 lambda
[](const auto & node) { return node.Score2(1.0); }
我得出的结论是 lambda 的优化优于 std::mem_fn
取此代码:
#include <iostream>
#include <array>
#include <functional>
class Node {
private:
double val;
public:
Node(double val) noexcept : val(val) {}
[[nodiscard]] auto Score() const noexcept {
return 10.0 / val;
}
};
template <typename FwdIt, typename Eval, typename Pred = std::less<>>
constexpr FwdIt max_eval_element(FwdIt first, FwdIt last, Eval eval, Pred pred = Pred()) {
FwdIt found = first;
if (first != last) {
auto best = eval(*found);
while (++first != last) {
if (auto const thisVal = eval(*first);
pred(best, thisVal)) {
found = first;
best = thisVal;
}
}
}
return found;
}
int main()
{
std::array<Node, 10> nodes{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
auto nodeIt1 = max_eval_element(std::cbegin(nodes), std::cend(nodes), std::mem_fn(&Node::Score));
std::cout << "dist1 " << std::distance(std::cbegin(nodes), nodeIt1) << std::endl;
auto nodeIt2 = max_eval_element(std::cbegin(nodes), std::cend(nodes), [](Node const& node) { return node.Score(); });
std::cout << "dist2 " << std::distance(std::cbegin(nodes), nodeIt2) << std::endl;
}
如果您查看编译后的程序集(例如 on GodBolt,使用 -O2 ),std:mem_fn 结果是一个函数调用
42 movsd QWORD PTR [rsp+8], xmm1
43 call Node::Score() const
44 movsd xmm1, QWORD PTR [rsp+8]
,而 lambda 是内联的
69 movsd xmm1, QWORD PTR .LC0[rip]
70 divsd xmm1, QWORD PTR [rax]
所以我一直在研究使用抽象算法在我的代码中重用重复出现的模式。具体来说,我想确定节点数组中具有最高 'score' 的元素,这是通过评估复杂的评分成员函数来确定的。
在
template <typename FwdIt, typename Eval, typename Pred = std::less<>>
constexpr FwdIt max_eval_element(FwdIt first, FwdIt last, Eval eval, Pred pred = Pred()) {
FwdIt found = first;
if (first != last) {
auto best = eval(*found);
while (++first != last) {
if (auto const thisVal = eval(*first);
pred(best, thisVal)) {
found = first;
best = thisVal;
}
}
}
return found;
}
所以考虑我的节点 class:
class Node {
private:
double val;
public:
Node(double val) noexcept : val(val) {}
[[nodiscard]] auto Score1() const noexcept {
return std::sqrt(std::log(10.0 / val));
}
[[nodiscard]] auto Score2(double other) const noexcept {
return std::sqrt(std::log(other / val));
}
};
和我的节点数组:
std::array<Node, 100000> nodes;
我可以打电话
auto const& Node = *std::max_eval_element(std::cbegin(nodes), std::cend(nodes), std::mem_fn(&Node::Score1));
但现在我想为 Score2
重复此操作,其中输入取决于一些局部变量...当然我可以编写一些 lambda 函数...但我们有 std::bind
那个,对吧?我知道你可以在
std::bind(this, std::mem_fn(Node::Score1));
但我想要的是反过来。哪个不行。
auto const& Node = *std::max_eval_element(std::cbegin(nodes), std::cend(nodes), std::bind(std::mem_fn(&Node::Score2), 1.0));
我换了个方法试了,还是不行
auto const& Node = *std::max_eval_element(std::cbegin(nodes), std::cend(nodes), std::mem_fn(std::bind(&Node::Score2), 1.0));
我知道为什么这不起作用...成员函数需要对象指针作为(隐藏的)第一个参数。但这将意味着我们缺少类似 std::bind_mem_fn
左右的东西...我们过去有 std::bind2nd
,但它已在 C++17 中删除...
再说一次:我当然可以使用 lambda,但考虑到 std:mem_fn
和 std::bind
之类的东西存在,抽象算法是一件好事...
我是不是遗漏了什么,或者这只是标准中遗漏的?
呼叫std::bind(&Node::Score2)
是问题所在。它缺少传递给 Score2
的参数。你想要:
std::bind(&Node::Score2, std::placeholders::_1, 1.0)
这不是指向成员的指针,因此它不是 std::mem_fn
或者,您可以使用 lambda
[](const auto & node) { return node.Score2(1.0); }
我得出的结论是 lambda 的优化优于 std::mem_fn
取此代码:
#include <iostream>
#include <array>
#include <functional>
class Node {
private:
double val;
public:
Node(double val) noexcept : val(val) {}
[[nodiscard]] auto Score() const noexcept {
return 10.0 / val;
}
};
template <typename FwdIt, typename Eval, typename Pred = std::less<>>
constexpr FwdIt max_eval_element(FwdIt first, FwdIt last, Eval eval, Pred pred = Pred()) {
FwdIt found = first;
if (first != last) {
auto best = eval(*found);
while (++first != last) {
if (auto const thisVal = eval(*first);
pred(best, thisVal)) {
found = first;
best = thisVal;
}
}
}
return found;
}
int main()
{
std::array<Node, 10> nodes{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
auto nodeIt1 = max_eval_element(std::cbegin(nodes), std::cend(nodes), std::mem_fn(&Node::Score));
std::cout << "dist1 " << std::distance(std::cbegin(nodes), nodeIt1) << std::endl;
auto nodeIt2 = max_eval_element(std::cbegin(nodes), std::cend(nodes), [](Node const& node) { return node.Score(); });
std::cout << "dist2 " << std::distance(std::cbegin(nodes), nodeIt2) << std::endl;
}
如果您查看编译后的程序集(例如 on GodBolt,使用 -O2 ),std:mem_fn 结果是一个函数调用
42 movsd QWORD PTR [rsp+8], xmm1
43 call Node::Score() const
44 movsd xmm1, QWORD PTR [rsp+8]
,而 lambda 是内联的
69 movsd xmm1, QWORD PTR .LC0[rip]
70 divsd xmm1, QWORD PTR [rax]