为什么有 std::not1() 和 std::not2() 而不是一个重载的 std::not_()?

why are there std::not1() and std::not2() rather than a single overloaded std::not_()?

C++ std 命名空间分别包含辅助函数 std::not1 and std::not2. They both take a unary or binary predicate functor, respectively, and return a std::unary_negate or std::binary_negate 谓词。

我想知道是否不应该使用一些模板魔术来拥有

template<typename Predicate> inline
enable_if_t<is_unary_predicate<Predicate>::value, unary_negate<Predicate> >
not_(Predicate const&pred)
{ return unary_negate<Predicate>{pred}; }

template<typename Predicate> inline
enable_if_t<is_binary_predicate<Predicate>::value, binary_negate<Predicate> >
not_(Predicate const&pred)
{ return binary_negate<Predicate>{pred}; }

区分参数 pred 传递给 return 适当的谓词。当然,也有一些奇怪的情况,即传递的对象 pred 具有两种类型的运算符(一元和二元),这将不起作用,但可以在不使用此辅助函数的情况下处理这些情况。

在没有 C++11 功能的情况下计算出正确的重载并非易事。在设计STL和提出这些函数objects的时候,甚至没有一个编译器能够编译其中的一些函数。结果,一些功能比原本更难使用。例如,创建一个 std::not_() 函数是完全可行的(std::not() 是不可能的,因为 not 恰好是一个替代标记,因此不是一个可行的函数名称).即答案是:多为历史偶然

很可能 std::not1std::not2 是在函数重载的偏序规则还很模糊的时候提出的。 STL 的主要提案是在 1994 年或 1995 年完成的(我无法在 mailing archive 中快速找到它)。如果为了响应 STL 提案而实际更改过载规则,我不会感到惊讶。

也就是说,其他人花了几年时间才跟上速度并开发出这些界面的改进版本。 Boost 引领了这些发展。

关于实现魔术,创建一个可以处理各种参数的 not_ 函数实际上可能非常简单:

template <typename Pred>
class not_fn_t {
    std::decay_t<Pred> pred;
public:
    explicit not_fn_t(Pred p): pred(p) {}
    template <typename... A>
    bool operator()(A&&... a) const {
        return !this->pred(std::forward<A>(a)...);
    }
};
template <typename Pred>
not_fn_t<Pred> not_fn(Pred&& pred) {
    return not_fn_t<Pred>(std::forward<Pred>(pred));
}

实际上,这几乎就是在上次会议上作为 std::not_fn() 投票进入 C++ 工作文件的内容。这是一个 C++11 公式,但道德上的等价物可以用早期版本的 C++ 完成,只需为每个支持的元数扩展函数调用运算符(显然,没有完美转发)。

2013 年有一个类似的提议:N3699 A proposal to add a generalized callable negator. This has bumped along for some time (most recent version is N4022) and looks like it should make it into the second Library Fundamentals TS; it's present in section func.not_fn in Library Fundamentals TS 2 draft n4564

标准中存在 not1not2 的原因是它们已经存在了很长一段时间,因为在元编程技术存在之前,支持单个否定器是必需的。