如何'std::bind()'一个标准的库算法
How to `std::bind()` a standard library algorithm?
我的问题的简短版本是这样的:如何将std::bind()
之类的东西与标准库算法一起使用?
由于简短版本有点缺乏细节,这里有一些解释:假设我有算法std::transform()
,现在我想实现std::copy()
(是的,我意识到标准C++库中有std::copy()
)。由于我非常懒惰,我显然想使用现有的std::transform()
实现。当然,我可以这样做:
struct identity {
template <typename T>
auto operator()(T&& value) const -> T&& { return std::forward<T>(value); }
};
template <typename InIt, typename OutIt>
auto copy(InIt begin, InIt end, OutIt to) -> OutIt {
return std::transform(begin, end, to, identity());
}
不知何故,这种实现有点像算法的配置。例如,似乎std::bind()
应该能够完成这项工作,但简单地使用std::bind()
不起作用:
namespace P = std::placeholders;
auto copy = std::bind(std::transform, P::_1, P::_2, P::_3, identity());
问题是编译器无法仅从算法中确定适当的模板参数,并且是否存在&
并不重要。有没有办法使像使用std::bind()
这样的方法起作用?由于这是期待的,我对解决方案感到满意,该解决方案适用于已经提议包含在C++标准中的任何内容。另外,为了摆脱我的懒惰,我很乐意提前做一些工作,以便以后更容易使用。可以这样想:作为库实现者,我会把事情放在一起一次,这样每个库用户都可以偷懒:我是一个忙碌的实现者,但也是一个懒惰的用户。
如果您想拥有一个现成的测试台:这是一个完整的程序。
#include <algorithm>
#include <functional>
#include <iostream>
#include <iterator>
#include <utility>
#include <vector>
using namespace std::placeholders;
struct identity {
template <typename T>
T&& operator()(T&& value) const { return std::forward<T>(value); }
};
int main()
{
std::vector<int> source{ 0, 1, 2, 3, 4, 5, 6 };
std::vector<int> target;
#ifdef WORKS
std::transform(source.begin(), source.end(), std::back_inserter(target),
identity());
#else
// the next line doesn't work and needs to be replaced by some magic
auto copy = std::bind(&std::transform, _1, _2, _3, identity());
copy(source.begin(), source.end(), std::back_inserter(target));
#endif
std::copy(target.begin(), target.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << "n";
}
当尝试std::bind()
重载函数时,编译器无法确定要使用哪个重载:在计算bind()
-表达式时,函数参数是未知的,即重载解析无法决定选择哪个重载。在C++[还?]中没有直接的方法将重载集视为对象。函数模板只是生成一个重载集,每个可能的实例化都有一个重载。也就是说,无法std::bind()
任何标准C++库算法的整个问题都围绕着标准库算法是函数模板的事实。
一种与std::bind()
算法具有相同效果的方法是使用 C++14 个通用 lambda 进行绑定,例如:
auto copy = [](auto&&... args){
return std::transform(std::forward<decltype(args)>(args)..., identity());
};
虽然这有效,但它实际上等效于函数模板的花哨实现,而不是配置现有函数。但是,使用通用 lambda 在合适的标准库命名空间中创建主函数对象可以使实际的底层函数对象随时可用,例如:
namespace nstd {
auto const transform = [](auto&&... args){
return std::transform(std::forward<decltype(args)>(args...));
};
}
现在,使用实现transform()
的方法实际上使用std::bind()
来构建copy()
是微不足道的:
auto copy = std::bind(nstd::transform, P::_1, P::_2, P::_3, identity());
尽管通用 lambda 的外观和使用,但值得指出的是,仅使用 C++11 可用的功能创建相应的函数对象实际上需要大致相同的努力:
struct transform_t {
template <typename... Args>
auto operator()(Args&&... args) const
-> decltype(std::transform(std::forward<decltype(args)>(args)...)) {
return std::transform(std::forward<decltype(args)>(args)...);
}
};
constexpr transform_t transform{};
是的,它更多的是类型化,但它只是使用通用 lambda 的一个合理的小常量因素,即,如果使用通用 lambda 的对象,C++11 版本也是如此。
当然,一旦我们有了算法的函数对象,实际上甚至不必std::bind()
它们可能是很好的,因为我们需要提及所有未绑定的参数。在示例案例中,它是 currying(好吧,我认为 currying 仅适用于绑定第一个参数,但它是第一个还是最后一个参数似乎有点随机)。如果我们有curry_first()
和curry_last()
来讨好第一个或最后一个论点呢?curry_last()
的实现也是微不足道的(为简洁起见,我使用的是通用 lambda,但可以使用与上述相同的重写来使其可用于 C++11):
template <typename Fun, typename Bound>
auto curry_last(Fun&& fun, Bound&& bound) {
return [fun = std::forward<Fun>(fun),
bound = std::forward<Bound>(bound)](auto&&... args){
return fun(std::forward<decltype(args)>(args)..., bound);
};
}
现在,假设curry_last()
位于同一个命名空间中,nstd::transform
或identity()
copy()
的定义可以变为:
auto const copy = curry_last(nstd::transform, identity());
好吧,也许这个问题没有让我有任何帽子,但也许我会得到一些支持,将我们的标准库算法转换为函数对象,并可能添加一些很酷的方法来创建所述算法的绑定版本。我认为这种方法比该领域的一些建议要理智得多(尽管以上述形式可能不那么完整)。
- 如果要求比较器是严格的总排序,而不仅仅是严格的弱排序,C++标准算法会更快吗?
- 如何在不使用标准算法的情况下在排序向量中添加 c 元素?
- NTRUEncrypt:使用开源标准算法中的描述无法正确找到两个多项式的GCD,无法定义是否存在多边形的逆
- 在标准算法中,通过引用捕获更正确
- 是否存在用于按以下方式对两个范围进行排序和分区的标准算法?
- 传递标准算法.模板推导
- 标准算法库中的哪些算法进行分配?有没有办法指定这种分配是如何发生的
- 标准算法按值获取 lambda 是否有原因?
- 是否有标准算法要复制直到
- 是否有有效的标准算法来栅格化面,包括其内部区域
- 是否可以混合使用虚拟函数和函数对象以使用标准算法
- 如何使用标准算法重新实现这一点
- 标准算法 any_of()、all_of() 和 none_of() 应用于空范围
- 是否可以以优雅的方式在向量上运行标准算法<MyType>?
- 它是否定义为为C++标准算法提供反转范围
- 它被定义为向C++标准算法提供一个空范围吗
- c++标准算法中的ForwardIterator和OutputIterator是否可以相同?
- 输出或遍历集合的所有对组合的标准算法
- c++标准算法,用最低优先级的运算符分隔一个表达式为两个表达式
- C++使用带字符串的标准算法,带isdigit的count_if,函数转换