为什么几个标准运算符没有标准函子? [英] Why do several of the standard operators not have standard functors?

查看:76
本文介绍了为什么几个标准运算符没有标准函子?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们有:

  • 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(>=)
  • 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[]
  • 所有成员函数指针运算符
  • 所有复合赋值运算符.
  • & (address-of)
  • * (dereference)
  • []
  • ,
  • bitwise operators ~, &, |, ^, <<, >>
  • ++ (prefix/postfix) / -- (prefix/postfix)
  • sizeof
  • static_cast / dynamic_cast / reinterpret_cast / const_cast
  • c style casts
  • new / new[] / delete / delete[]
  • all of the member function pointer operators
  • all of the compound assignment operators.

是没有原因吗?还是仅仅是疏忽?

Is there a reason we don't have those, or is it just an oversight?

推荐答案

我认为该问题最可能的答案是所包含的运算符是被认为最有用的运算符.如果没有人考虑将某些内容添加到标准库中,则不会添加任何内容.

I think the most likely answer to the question is that the included operators are the ones that were thought to be most useful. If no one thinks to add something to the Standard Library, it won't get added.

我认为断言运算符在C ++ 0x中是无用的,因为lambda表达式优越,这是愚蠢的:当然,lambda表达式很棒而且更加灵活,但是有时使用命名的functor可以使代码更简洁,更简洁,更容易理解的代码;此外,命名函子可以是多态的,而lambda则不能.

I think the assertion that the operator functors are useless in C++0x because lambda expressions are superior is silly: sure, lambda expressions are wonderful and far more flexible, but sometimes using a named functor can lead to terser, cleaner, easier to understand code; in addition, named functors can be polymorphic while lambdas cannot.

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

The Standard Library operator functors are, of course, not polymorphic (they are class templates, so the operand types are part of the type of the functor). It's not particularly difficult to write your own operator functors, though, and macros make the task quite straightforward:

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;
        }
    };
}

我已经省略了newdelete(以及它们的各种形式),因为用它们编写异常安全的代码太困难了:-).

I have omitted new and delete (and their various forms) because it is too difficult to write exception-safe code with them :-).

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

The call implementation is limited to nullary operator() overloads; with variadic templates you might be able to extend this to support a wider range of overloads, but really you'd be better off using lambda expressions or a library (like std::bind) to handle more advanced call scenarios. The same goes for the .* and ->* implementations.

提供了剩余的可重载运算符,甚至包括愚蠢的sizeofthrow.

The remaining overloadable operators are provided, even the silly ones like sizeof and throw.

[上面的代码是独立的;不需要标准库头.我承认我对右值引用还算是个菜鸟,所以如果我对它们做错了事,希望有人能让我知道.]

[The above code is standalone; no Standard Library headers are required. I admit I am a bit of a noob still with respect to rvalue references, so if I've done something wrong with them I hope someone will let me know.]

这篇关于为什么几个标准运算符没有标准函子?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆