我认为这个问题最有可能的答案是包含的运算符是被认为最有用的运算符。如果没有人想向标准库添加一些东西,它就不会被添加。
我认为断言操作符仿函数在 C++0x 中是无用的,因为 lambda 表达式更优越是愚蠢的:当然,lambda 表达式很棒而且更灵活,但有时使用命名仿函数会导致更简洁、更简洁、更容易理解代码;此外,命名函子可以是多态的,而 lambda 则不能。
当然,标准库运算符函子不是多态的(它们是类模板,因此操作数类型是函子类型的一部分)。不过,编写自己的运算符函子并不是特别困难,而且宏使任务变得非常简单:
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;
}
};
}
我省略了new 和delete(以及它们的各种形式),因为用它们编写异常安全代码太难了:-)。
call 实现仅限于无效的operator() 重载;使用可变参数模板,您可以扩展它以支持更广泛的重载,但实际上您最好使用 lambda 表达式或库(如std::bind)来处理更高级的调用场景。 .* 和 ->* 实现也是如此。
提供了剩余的可重载运算符,即使是像sizeof 和throw 这样愚蠢的运算符。
[以上代码是独立的;不需要标准库头文件。我承认在右值引用方面我还是个菜鸟,所以如果我对它们做错了什么,我希望有人能告诉我。]