Я думаю, что наиболее вероятный ответ на этот вопрос заключается в том, что включенные операторы считаются наиболее полезными.Если никто не думает добавить что-то в Стандартную библиотеку, это не будет добавлено.
Я думаю, что утверждение о том, что операторные функторы бесполезны в C ++ 0x, потому что лямбда-выражения превосходят, глупо: конечно,Лямбда-выражения замечательны и гораздо более гибки, но иногда использование именованного функтора может привести к более короткому, понятному и простому для понимания коду;Кроме того, именованные функторы могут быть полиморфными, а лямбды - нет.
Функторы операторов стандартной библиотеки, конечно, не являются полиморфными (они являются шаблонами классов, поэтому типы операндов являются частью типа функтора).Тем не менее, написать свои собственные оператор-функторы не составляет особого труда, и макросы делают задачу довольно простой:
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()
;с помощью шаблонов с переменным числом аргументов вы могли бы расширить это для поддержки более широкого диапазона перегрузок, но на самом деле вам лучше использовать лямбда-выражения или библиотеку (например, std::bind
) для обработки более сложных сценариев вызовов.То же самое касается реализаций .*
и ->*
.
Предоставляются остальные перегружаемые операторы, даже глупые, такие как sizeof
и throw
.
[Код вышеавтономный;заголовки стандартной библиотеки не требуются.Я признаю, что я все еще немного новичок в отношении ссылок на rvalue, поэтому, если я сделал с ними что-то не так, я надеюсь, что кто-то сообщит мне.]