如何解决为什么几个标准运算符没有标准函子?
| 我们有: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
(>=
)
我们没有以下函子:
30ѭ(地址)
*
(取消引用)
[]
,
按位运算符~
,&
,|
,^
,<<
,>>
++
(前缀/后缀)/--
(前缀/后缀)
sizeof
static_cast
/dynamic_cast
/reinterpret_cast
/const_cast
c风格的演员
new
/new[]
/delete
/delete[]
所有成员函数指针运算符
所有复合赋值运算符。
我们没有这些是有原因的,还是仅仅是疏忽?
解决方法
我认为该问题最可能的答案是所包含的运算符是被认为最有用的运算符。如果没有人考虑将某些内容添加到标准库中,则不会添加任何内容。
我认为断言运算符在C ++ 0x中是无用的,因为lambda表达式优越,这很愚蠢:可以肯定的是,lambda表达式很棒而且更灵活,但是有时使用命名的functor可以使代码更简洁,更清晰,更易于理解码;此外,命名函子可以是多态的,而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,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
的实现仅限于无效的ѭ55;重载;使用可变参数模板,您也许可以扩展它以支持更大范围的重载,但实际上,最好使用lambda表达式或库(如std::bind
)来处理更高级的调用方案。 ѭ57和ѭ58实现也是如此。
提供了剩余的可重载运算符,甚至是愚蠢的诸如sizeof
和throw
。
[以上代码是独立的;不需要标准库头。我承认对于右值引用我还是有点菜鸟,所以如果我对它们做错了事,希望有人能告诉我。]
, 原因可能是大多数开发人员不需要它们。其他人使用Boost.Lambda,其中大多数都在那里。
, 极有可能的是,标准委员会中没有人认为它们会有用。有了C ++ 0x的lambda支持,它们都没有用。
编辑:
我并不是说他们没有用,更多的是委员会上的任何人都没有真正想到过这种用处。
, 在C ++ 0x中添加了按位运算符。我还发现not_equal_to已经存在。
其他(例如sizeof和某些强制转换)是编译时运算符,因此在函子中用处不大。
new和delete在分配器中抽象。我们需要更多吗?
, say47ѭ每个发言都没有函子,但是默认分配器确实将请求传递给pass47ѭ。由于两者已链接,因此也涵盖了covers49ѭ。
从那里开始,我认为这些仿函数并不是真的要被视为“您传递给for_each
的东西,而是”您可能需要根据具体情况专门研究的东西。”
从列表中删除new
[和家族],基本上您可以进行一堆除了语言指定之外没有实际意义的操作。如果采用对象的地址,则实际上只有一件事要发生:您将获得该对象的地址。可能会改变,但不会改变。因此,实际上并不需要通过标准函子来专门化该行为。您可以只使用&
并信任运算符重载来执行此操作。但是\“ add \”或\“ compare \”的含义可能会在程序执行过程中发生变化,因此提供一种实现方法的优点。
这也包括复合赋值运算符;它们的含义与它们的两个部分相关,因此,如果您需要std::add_assign
,就可以退回到std::add
[和operator =
,这是您列表中没有的]。
按位运算符介于两者之间;我可以以任何一种方式看到他们的论点。
,列出的所有参数都是两个参数的函子。并非以下所有内容。实际上,只有>>
,<<
,&
,|
和!=
满足该标准,并且就函子而言,它们的用处不大。强制转换尤其是模板,这使得它们的使用性降低了。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。