Boost C++ 库

...世界上最受推崇和专业设计的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu, C++ 编码标准

PrevUpHomeNext

运算符类型特征

简介

这些特征都是 值特征,继承自 integral_constant 并提供一个简单的 truefalse 布尔值 value,它反映了给定类型是否可以与给定的运算符一起使用。

例如,has_plus<int, double>::value 是一个 bool 类型的值,其值为 true,因为可以将 double 类型添加到 int 类型,如下面的代码所示

int i;
double d;
i+d;

也可以知道运算符的结果是否可以用作给定类型的函数参数。例如,has_plus<int, double, float>::valuetrue,因为可以将 double 类型添加到 int 类型,并且结果 (double) 可以转换为 float 参数,如下面的代码所示

void f(float) { };
int i;
double d;
f(i+d);

应用示例

这些特征可用于优化支持给定操作的类型的代码。例如,一个函数 std::advance,用于将迭代器增加给定步数,可以如下实现

#include <boost/type_traits/has_plus_assign.hpp>

namespace detail {
template < class Iterator, class Distance, bool has_plus_assign >
struct advance_impl;

// this is used if += exists (efficient)
template < class Iterator, class Distance >
struct advance_impl<Iterator, Distance, true> {
   void operator()(Iterator &i, Distance n) {
      i+=n;
   }
};

// this is use if += does not exists (less efficient but cannot do better)
template < class Iterator, class Distance >
struct advance_impl<Iterator, Distance, false> {
   void operator()(Iterator &i, Distance n) {
      if (n>0) {
         while (n--) ++i;
      } else {
         while (n++) --i;
      }
   }
};
} // namespace detail

template < class Iterator, class Distance >
inline void advance(Iterator &i, Distance n) {
   detail::advance_impl< Iterator, Distance, ::boost::has_plus_assign<Iterator>::value >()(i, n);
}

然后,编译器根据类型执行 += 运算的能力选择最有效的实现

#include <iostream>

class with {
      int m_i;
   public:
      with(int i=0) : m_i(i) { }
      with &operator+=(int rhs) { m_i+=rhs; return *this; }
      operator int const () { return m_i; }
};

class without {
      int m_i;
   public:
      without(int i=0) : m_i(i) { }
      without &operator++() { ++m_i; return *this; }
      without &operator--() { --m_i; return *this; }
      operator int const () { return m_i; }
};

int main() {
   with i=0;
   advance(i, 10); // uses +=
   std::cout<<"with: "<<i<<'\n';
   without j=0;
   advance(j, 10); // uses ++
   std::cout<<"without: "<<j<<'\n';
   return 0;
}

描述

语法如下

template < class Rhs, class Ret=dont_care > has_op; // prefix operator
template < class Lhs, class Ret=dont_care > has_op; // postfix operator
template < class Lhs, class Rhs=Lhs, class Ret=dont_care > has_op; // binary operator

其中

默认行为 (Ret=dont_care) 是不检查运算符的返回值。如果 Ret 与默认值 dont_care 不同,则检查返回值是否可转换为 Ret。可转换为 Ret 意味着返回值可以用作期望 Ret 的函数的参数

void f(Ret);
Lhs lhs;
Rhs rhs;
f(lhs+rhs); // is valid if has_plus<Lhs, Rhs, Ret>::value==true

如果 Ret=void,则检查返回类型是否完全为 void

下表给出了支持的二元、前缀和后缀运算符的列表。

表 1.4. 支持的前缀运算符

前缀运算符

特征名称

!

has_logical_not < class Rhs, class Ret=dont_care >

+

has_unary_plus

-

has_unary_minushas_negate

~

has_complement

*

has_dereference

++

has_pre_increment

--

has_pre_decrement


表 1.5. 支持的后缀运算符

后缀运算符

特征名称

++

has_post_increment < class Lhs, class Ret=dont_care >

--

has_post_decrement


表 1.6. 支持的二元运算符


以下运算符由于无法使用相同的技术实现而不被支持:operator=, operator->, operator&, operator[], operator,, operator(), operator new

cv 限定符和引用

运算符参数中的引用符号 & 被忽略,因此 has_plus< int&, double& >::value==has_plus< int, double >::value。之所以选择这样做,是因为如果以下代码有效(无效)

int i;
double d;
i+d;

则以下代码也有效(无效)

int &ir=i;
double &dr=d;
ir+dr;

无法正确处理 volatile 限定符,因此任何使用此限定符的构造都具有未定义的行为。

作为帮助,下表给出了每个特征模板参数的必要条件,以使特征 valuetrue。它们是非充分条件,因为条件必须对所有参数和返回类型都为 truevalue 才能为 true

表 1.7. 运算符参数为真的必要非充分条件

运算符声明

has_op< void >

has_op< Arg >has_op< Arg& >

has_op< Arg const >has_op< Arg const& >

operator@(Arg)

false

true

true

operator@(Arg const)

false

true

true

operator@(Arg &)

false

true

false

operator@(Arg const &)

false

true

true


表 1.8. 运算符返回类型为真的必要非充分条件

运算符声明

has_op< ..., void >

has_op< ..., Ret >

has_op< ..., Ret const >

has_op< ..., Ret & >

has_op< ..., Ret const & >

void operator@(...)

true

false

false

false

false

Ret operator@(...)

false

true

true

false

true

Ret const operator@(...)

false

true

true

false

true

Ret & operator@(...)

false

true

true

true

true

Ret const & operator@(...)

false

true

true

false

true


实现

实现仅包含头文件。应首先包含以下头文件

#include <boost/type_traits/has_operator.hpp>

#include <boost/type_traits/has_op.hpp>

其中 op 是为所需运算符选择的文本名称。第一种方法包含所有运算符特征。

所有特征都以相同的方式实现,使用预处理器宏来避免代码重复。主要文件位于 boost/type_traits/detail 中:has_binary_operator.hpphas_prefix_operator.hpphas_postfix_operator.hpp

给定一个充分符合 C++11 标准的编译器,这些特征以相当紧凑和直接的方式实现,应该始终给出准确的答案。

在 C++03 中,有一个遗留实现,例如,下面展示了前缀 operator-

namespace boost {
namespace detail {

// This namespace ensures that argument-dependent name lookup does not mess things up.
namespace has_unary_minus_impl {

// 1. a function to have an instance of type T without requiring T to be default
// constructible
template <typename T> T &make();


// 2. we provide our operator definition for types that do not have one already

// a type returned from operator- when no such operator is
// found in the type's own namespace (our own operator is used) so that we have
// a means to know that our operator was used
struct no_operator { };

// this class allows implicit conversions and makes the following operator
// definition less-preferred than any other such operators that might be found
// via argument-dependent name lookup
struct any { template <class T> any(T const&); };

// when operator- is not available, this one is used
no_operator operator-(const any&);


// 3. checks if the operator returns void or not
// conditions: Rhs!=void

// we first redefine "operator," so that we have no compilation error if
// operator- returns void and we can use the return type of
// (-rhs, returns_void_t()) to deduce if operator- returns void or not:
// - operator- returns void   -> (-rhs, returns_void_t()) returns returns_void_t
// - operator- returns !=void -> (-rhs, returns_void_t()) returns int
struct returns_void_t { };
template <typename T> int operator,(const T&, returns_void_t);
template <typename T> int operator,(const volatile T&, returns_void_t);

// this intermediate trait has member value of type bool:
// - value==true  -> operator- returns void
// - value==false -> operator- does not return void
template < typename Rhs >
struct operator_returns_void {
   // overloads of function returns_void make the difference
   // yes_type and no_type have different size by construction
   static ::boost::type_traits::yes_type returns_void(returns_void_t);
   static ::boost::type_traits::no_type returns_void(int);
   static const bool value = sizeof(::boost::type_traits::yes_type)==sizeof(returns_void((-make<Rhs>(),returns_void_t())));
};


// 4. checks if the return type is Ret or Ret==dont_care
// conditions: Rhs!=void

struct dont_care { };

template < typename Rhs, typename Ret, bool Returns_void >
struct operator_returns_Ret;

template < typename Rhs >
struct operator_returns_Ret < Rhs, dont_care, true > {
   static const bool value = true;
};

template < typename Rhs >
struct operator_returns_Ret < Rhs, dont_care, false > {
   static const bool value = true;
};

template < typename Rhs >
struct operator_returns_Ret < Rhs, void, true > {
   static const bool value = true;
};

template < typename Rhs >
struct operator_returns_Ret < Rhs, void, false > {
   static const bool value = false;
};

template < typename Rhs, typename Ret >
struct operator_returns_Ret < Rhs, Ret, true > {
   static const bool value = false;
};

// otherwise checks if it is convertible to Ret using the sizeof trick
// based on overload resolution
// condition: Ret!=void and Ret!=dont_care and the operator does not return void
template < typename Rhs, typename Ret >
struct operator_returns_Ret < Rhs, Ret, false > {
   static ::boost::type_traits::yes_type is_convertible_to_Ret(Ret); // this version is preferred for types convertible to Ret
   static ::boost::type_traits::no_type is_convertible_to_Ret(...); // this version is used otherwise

   static const bool value = sizeof(is_convertible_to_Ret(-make<Rhs>()))==sizeof(::boost::type_traits::yes_type);
};


// 5. checks for operator existence
// condition: Rhs!=void

// checks if our definition of operator- is used or an other
// existing one;
// this is done with redefinition of "operator," that returns no_operator or has_operator
struct has_operator { };
no_operator operator,(no_operator, has_operator);

template < typename Rhs >
struct operator_exists {
   static ::boost::type_traits::yes_type check(has_operator); // this version is preferred when operator exists
   static ::boost::type_traits::no_type check(no_operator); // this version is used otherwise

   static const bool value = sizeof(check(((-make<Rhs>()),make<has_operator>())))==sizeof(::boost::type_traits::yes_type);
};


// 6. main trait: to avoid any compilation error, this class behaves
// differently when operator-(Rhs) is forbidden by the standard.
// Forbidden_if is a bool that is:
// - true when the operator-(Rhs) is forbidden by the standard
//   (would yield compilation error if used)
// - false otherwise
template < typename Rhs, typename Ret, bool Forbidden_if >
struct trait_impl1;

template < typename Rhs, typename Ret >
struct trait_impl1 < Rhs, Ret, true > {
   static const bool value = false;
};

template < typename Rhs, typename Ret >
struct trait_impl1 < Rhs, Ret, false > {
   static const bool value =
      ::boost::type_traits::ice_and<
         operator_exists < Rhs >::value,
         operator_returns_Ret < Rhs, Ret, operator_returns_void < Rhs >::value >::value
      >::value
   ;
};

// specialization needs to be declared for the special void case
template < typename Ret >
struct trait_impl1 < void, Ret, false > {
   static const bool value = false;
};

// defines some typedef for convenience
template < typename Rhs, typename Ret >
struct trait_impl {
   typedef typename ::boost::remove_reference<Rhs>::type Rhs_noref;
   typedef typename ::boost::remove_cv<Rhs_noref>::type Rhs_nocv;
   typedef typename ::boost::remove_cv< typename ::boost::remove_reference< typename ::boost::remove_pointer<Rhs_noref>::type >::type >::type Rhs_noptr;
   static const bool value = trait_impl1 < Rhs_noref, Ret, ::boost::is_pointer< Rhs_noref >::value >::value;
};

} // namespace impl
} // namespace detail

// this is the accessible definition of the trait to end user
template < typename Rhs, typename Ret=::boost::detail::has_unary_minus_impl::dont_care >
struct has_unary_minus : ::boost::integral_constant<bool,(::boost::detail::has_unary_minus_impl::trait_impl < Rhs, Ret >::value)> { };

} // namespace boost

局限性
已知问题
致谢

Frederic Bron 非常感谢 boost 邮件列表中的许多人的友好帮助和耐心。特别是,以下人员对实现非常有帮助:Edward Diener, Eric Niebler, Jeffrey Lee Hellrung (Jr.), Robert Stewart, Roman Perepelitsa, Steven Watanabe, Vicente Botet。


PrevUpHomeNext