我们为什么需要unary_function和binary_function?

12
4个回答

13

这些不是函数,而是类(实际上是结构体,但这并不重要)。当你定义自己的二元函数以用于STL算法时,你从这些类派生它们,以便自动获取所有typedef。

例如:

struct SomeFancyUnaryFunction: public std::unary_function<Arg_t, Result_t>
{
   Result_t operator ()(Arg_t const &)
   {
      ...
   }
};

现在你无需手动提供argument_typeresult_type等typedef。这些结构体就像iterator结构体一样方便我们重复使用算法所需的typedef。

C++11更新:

从C++11开始,新的std::bind实际上不需要任何typedef,因此它们在某种程度上已过时。


1
operator(Arg_t const &) 应该替换为 operator()(Arg_t const &),或者我有什么遗漏了吗? - Luca Martini

7
基本上,它们提供了所有typedef,以允许使用函数适配器从一元和二元函数对象组合高阶函数。例如,这允许在需要一元函数的位置使用二元函数对象,将其中一个参数绑定到文字值:
std::find_if( begin, end, std::bind1st(greater<int>(),42) );

std::bind1st 依赖于传递给它的函数对象来提供这些类型。

据我所知,新的 std::bind 不需要它们,因此在新代码中,您可以使用 std::bind 并且不需要使用它们。


6

sgi STL文档中有一个关于函数对象的解释。简而言之,unary_function和binary_function用于使函数对象适配。这使它们可以与函数对象适配器(如unary_negate)一起使用。


4

它们是什么?

std::unary_functionstd::binary_function是创建可适应函数对象的基础结构体。 "可适应"一词意味着它们提供了必要的typedef,以便与标准函数适配器(如std::not1std::not2std::bind1ststd::bind2nd)一起使用。

何时需要使用它们?

每当您需要将自定义函数对象与标准函数适配器一起使用时,都可以使用它们。

有例子吗?

让我们考虑一些示例(我知道它们是人工的,但从另一方面来说,我希望它们相当描述性)。

示例1。

假设您想打印一个向量中所有长度不小于特定阈值的字符串并将它们打印到std::cout中。

可以使用下一个函数对象:

class LengthThreshold
{
    public:
        LengthThreshold(std::size_t threshold) : threshold(threshold) {}

        bool operator()(const std::string& instance) const
        {
            return (instance.size() < threshold);
        }

    private:
        const std::size_t threshold;
};

现在的任务非常简单,可以通过std::remove_copy_if算法执行:

// std::size_t threshold is defined somewhere

std::remove_copy_if(some_strings.begin(), some_strings.end(),
        std::ostream_iterator<std::string>(std::cout, "\n"),
        LengthThreshold(threshold)
);

如果您想使用相同的函数对象打印所有小于阈值的字符串长度怎么办?
我们可以想到的明显解决方案是使用std :: not1函数适配器:
// std::size_t threshold is defined somewhere

std::remove_copy_if(some_strings.begin(), some_strings.end(),
        std::ostream_iterator<std::string>(std::cout, "\n"),
        std::not1(LengthThreshold(threshold))
);

事实上,上述代码无法编译,因为我们的LengthThreshold不可适应,并且没有必要的typedefs用于std::not1
为了使其可适应,我们需要从std::unary_function继承:
class LengthThreshold : public std::unary_function<std::string, bool> 
{
    // Function object's body remains the same
}

现在我们的第一个例子完美运行。

例子2。

让我们改变我们之前的例子。假设我们不想在函数对象内部存储一个阈值。在这种情况下,我们可以将函数对象从一元谓词更改为二元谓词:

class LengthThreshold : public std::binary_function<std::string, std::size_t, bool>
{
    public:
        bool operator()(const std::string& lhs, std::size_t threshold) const
        {
            return lhs.size() < threshold;
        }
};

并使用 std::bind2nd 函数适配器:

// std::size_t threshold is defined somewhere

std::remove_copy_if(some_strings.begin(), some_strings.end(),
        std::ostream_iterator<std::string>(std::cout, "\n"),
        std::bind2nd(LengthThreshold(), threshold)
);

C++11及以上版本怎么处理?

上述所有示例故意只使用了C++03

原因是C++11开始,std::unary_functionstd::binary_function被废弃,C++17中彻底删除。

这是由于更加通用和灵活的函数出现,例如std::bind,使得继承自std::unary_functionstd::binary_function变得多余。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接