为什么有几个标准运算符没有标准仿函数?

Bil*_*eal 23 c++ stl functor

我们有:

  • std::plus (+)
  • std::minus(-)
  • std::multiplies(*)
  • std::divides(/)
  • std::modulus(%)
  • std::negate(-)
  • std::logical_or(||)
  • std::logical_not(!)
  • std::logical_and(&&)
  • std::equal_to(==)
  • std::not_equal_to(!=)
  • std::less(<)
  • std::greater(>)
  • std::less_equal(<=)
  • std::greater_equal(>=)

我们没有仿函数:

  • & (地址)的
  • * (解引用)
  • []
  • ,
  • 位运算符~,&,|,^,<<,>>
  • ++(前缀/后缀)/ --(前缀/后缀)
  • sizeof
  • static_cast/ dynamic_cast/ reinterpret_cast/const_cast
  • c风格演员
  • new/ new[]/ delete/delete[]
  • 所有成员函数指针运算符
  • 所有复合赋值运算符.

有没有理由我们没有这些,或者只是一个疏忽?

Jam*_*lis 9

我认为这个问题最可能的答案是包含的运算符是被认为最有用的运算符.如果没有人想要在标准库中添加内容,则不会添加.

我认为运算符函数在C++ 0x中无用的断言因为lambda表达式更优秀是愚蠢的:当然,lambda表达式很精彩且更灵活,但有时使用命名的仿函数可以导致更简洁,更清晰,更容易理解码; 另外,命名函子可以是多态的,而lambdas则不能.

当然,标准库操作符仿函数不是多态的(它们是类模板,因此操作数类型是仿函数类型的一部分).但是,编写自己的运算符仿函数并不是特别困难,并且宏使任务变得非常简单:

namespace ops
{
    namespace detail
    {
        template <typename T>
        T&& declval();

        template <typename T>
        struct remove_reference      { typedef T type; }

        template <typename T>
        struct remove_reference<T&>  { typedef T type; }

        template <typename T>
        struct remove_reference<T&&> { typedef T type; }

        template <typename T>
        T&& forward(typename remove_reference<T>::type&& a)
        {
            return static_cast<T&&>(a);
        }

        template <typename T>
        T&& forward(typename remove_reference<T>::type& a)
        {
            return static_cast<T&&>(a);
        }

        template <typename T>
        struct subscript_impl
        {
            subscript_impl(T&& arg) : arg_(arg) {}

            template <typename U>
            auto operator()(U&& u) const ->
                decltype(detail::declval<U>()[detail::declval<T>()])
            {
                return u[arg_];
            }
        private:
            mutable T arg_;
        };
    }

    #define OPS_DEFINE_BINARY_OP(name, op)                              \
        struct name                                                     \
        {                                                               \
            template <typename T, typename U>                           \
            auto operator()(T&& t, U&& u) const ->                      \
                decltype(detail::declval<T>() op detail::declval<U>())  \
            {                                                           \
                return detail::forward<T>(t) op detail::forward<U>(u);  \
            }                                                           \
        }

    OPS_DEFINE_BINARY_OP(plus,               +  );
    OPS_DEFINE_BINARY_OP(minus,              -  );
    OPS_DEFINE_BINARY_OP(multiplies,         *  );
    OPS_DEFINE_BINARY_OP(divides,            /  );
    OPS_DEFINE_BINARY_OP(modulus,            %  );

    OPS_DEFINE_BINARY_OP(logical_or,         || );
    OPS_DEFINE_BINARY_OP(logical_and,        && );

    OPS_DEFINE_BINARY_OP(equal_to,           == );
    OPS_DEFINE_BINARY_OP(not_equal_to,       != );
    OPS_DEFINE_BINARY_OP(less,               <  );
    OPS_DEFINE_BINARY_OP(greater,            >  );
    OPS_DEFINE_BINARY_OP(less_equal,         <= );
    OPS_DEFINE_BINARY_OP(greater_equal,      >= );

    OPS_DEFINE_BINARY_OP(bitwise_and,        &  );
    OPS_DEFINE_BINARY_OP(bitwise_or,         |  );
    OPS_DEFINE_BINARY_OP(bitwise_xor,        ^  );
    OPS_DEFINE_BINARY_OP(left_shift,         << );
    OPS_DEFINE_BINARY_OP(right_shift,        >> );

    OPS_DEFINE_BINARY_OP(assign,             =  );
    OPS_DEFINE_BINARY_OP(plus_assign,        += );
    OPS_DEFINE_BINARY_OP(minus_assign,       -= );
    OPS_DEFINE_BINARY_OP(multiplies_assign,  *= );
    OPS_DEFINE_BINARY_OP(divides_assign,     /= );
    OPS_DEFINE_BINARY_OP(modulus_assign,     %= );
    OPS_DEFINE_BINARY_OP(bitwise_and_assign, &= );
    OPS_DEFINE_BINARY_OP(bitwise_or_assign,  |= );
    OPS_DEFINE_BINARY_OP(bitwise_xor_assign, ^= );
    OPS_DEFINE_BINARY_OP(left_shift_assign,  <<=);
    OPS_DEFINE_BINARY_OP(right_shift_assign, >>=);

    #define OPS_DEFINE_COMMA() ,
    OPS_DEFINE_BINARY_OP(comma, OPS_DEFINE_COMMA());
    #undef OPS_DEFINE_COMMA

    #undef OPS_DEFINE_BINARY_OP

    #define OPS_DEFINE_UNARY_OP(name, pre_op, post_op)                  \
    struct name                                                         \
    {                                                                   \
        template <typename T>                                           \
        auto operator()(T&& t) const ->                                 \
            decltype(pre_op detail::declval<T>() post_op)               \
        {                                                               \
            return pre_op detail::forward<T>(t) post_op;                \
        }                                                               \
    }

    OPS_DEFINE_UNARY_OP(dereference,      * ,   );
    OPS_DEFINE_UNARY_OP(address_of,       & ,   );
    OPS_DEFINE_UNARY_OP(unary_plus,       + ,   );
    OPS_DEFINE_UNARY_OP(logical_not,      ! ,   );
    OPS_DEFINE_UNARY_OP(negate,           - ,   );
    OPS_DEFINE_UNARY_OP(bitwise_not,      ~ ,   );
    OPS_DEFINE_UNARY_OP(prefix_increment, ++,   );
    OPS_DEFINE_UNARY_OP(postfix_increment,  , ++);
    OPS_DEFINE_UNARY_OP(prefix_decrement, --,   );
    OPS_DEFINE_UNARY_OP(postfix_decrement,  , --);
    OPS_DEFINE_UNARY_OP(call,               , ());
    OPS_DEFINE_UNARY_OP(throw_expr,   throw ,   );
    OPS_DEFINE_UNARY_OP(sizeof_expr, sizeof ,   );

    #undef OPS_DEFINE_UNARY_OP

    template <typename T>
    detail::subscript_impl<T> subscript(T&& arg)
    {
        return detail::subscript_impl<T>(detail::forward<T>(arg));
    }

    #define OPS_DEFINE_CAST_OP(name, op)                                \
        template <typename Target>                                      \
        struct name                                                     \
        {                                                               \
            template <typename Source>                                  \
            Target operator()(Source&& source) const                    \
            {                                                           \
                return op<Target>(source);                              \
            }                                                           \
        }

    OPS_DEFINE_CAST_OP(const_cast_to,       const_cast      );
    OPS_DEFINE_CAST_OP(dynamic_cast_to,     dynamic_cast    );
    OPS_DEFINE_CAST_OP(reinterpret_cast_to, reinterpret_cast);
    OPS_DEFINE_CAST_OP(static_cast_to,      static_cast     );

    #undef OPS_DEFINE_CAST_OP

    template <typename C, typename M, M C::*PointerToMember>
    struct get_data_member
    {
        template <typename T>
        auto operator()(T&& arg) const ->
            decltype(detail::declval<T>().*PointerToMember)
        {
            return arg.*PointerToMember;
        }
    };

    template <typename C, typename M, M C::*PointerToMember>
    struct get_data_member_via_pointer
    {
        template <typename T>
        auto operator()(T&& arg) const ->
            decltype(detail::declval<T>()->*PointerToMember)
        {
            return arg->*PointerToMember;
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

我省略了newdelete(及其各种形式),因为实在是太难了:-)与他们编写异常安全的代码.

call实施仅限于无参operator()过载; 使用可变参数模板,您可以扩展它以支持更广泛的重载,但实际上您最好使用lambda表达式或库(如std::bind)来处理更高级的调用方案.对于.*->*实现也是如此.

提供其余的重载操作,即使是像傻者sizeofthrow.

[以上代码是独立的; 不需要标准库头.我承认我在rvalue引用方面仍然是一个noob,所以如果我对它们做错了,我希望有人会让我知道.


Kir*_*sky 6

原因可能是大多数开发人员不需要它们.其他人使用Boost.Lambda,其中大多数都在那里.


Pup*_*ppy 5

最有可能的是,标准委员会中没有人认为它们会有用.有了C++ 0x的lambda支持,那么它们都没有用.

编辑:

我并不是说他们没有用 - 委员会中没有人真正想到这种用法.

  • 它们仍然有用,因为它们可以被覆盖特定类型,即使您无法更改类型的实现. (2认同)