Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

Operator Type Traits

Boost , Chapter 1. Boost.TypeTraits , Type Traits that Describe the Properties of a Type

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext
Introduction

Все эти черты являются.наследование отintegral_constantи предоставление простого<true>или<false>boolean<value>, что отражает тот факт, что данные типы могут или не могут использоваться с данными операторами.

Например,<has_plus<int,double>::value>— это<bool>, значение которого<true>, поскольку можно добавить<double>к<int>, как в следующем коде:

int i;
double d;
i+d;

Также можно узнать, можно ли использовать результат оператора в качестве аргумента функции данного типа. Например,<has_plus<int,double,float>::value>является<true>, потому что можно добавить<double>к<int>и результат<double>может быть преобразован в<float>аргумент, как в следующем коде:

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

Example of application

Эти черты могут быть полезны для оптимизации кода для типов, поддерживающих данные операции. Например, функция<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;
}

Description

Синтаксис заключается в следующем:

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

где:

  • op - имя оператора
  • <Lhs>- это тип, используемый с левой стороны<operator op>,
  • <Rhs>- вид, используемый с правой стороны<operator op>,
  • <Ret>— это тип, для которого мы хотим знать, может ли быть преобразован результат<operator op>.

Поведение по умолчанию<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>.

В следующих таблицах приведен список поддерживаемых бинарных, префиксных и постфиксных операторов.

Table 1.4. Supported prefix operators

префиксный оператор

название черты

<!>

<has_logical_not><<class Rhs, classRet=dont_care >>

<+>

<has_unary_plus>

<->

<has_unary_minus>и<has_negate>

<~>

<has_complement>

<*>

<has_dereference>

<++>

<has_pre_increment>

--

<has_pre_decrement>


Table 1.5. Supported postfix operators

оператор postix

название черты

<++>

<has_post_increment><<class Lhs, classRet=dont_care >>

--

<has_post_decrement>



Следующие операторы не поддерживаются, потому что они не могут быть реализованы с использованием той же техники:<operator=>,<operator->>,<operator&>,<operator[]>,<operator,>,<operator()>,<operator new>.

cv qualifiers and references

Ссылочный знак<&>в аргументе оператора игнорируется так, что<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>, чтобы любая конструкция, использующая этот квалификатор, имела неопределенное поведение.

В качестве помощи следующие таблицы дают необходимые условия для каждого аргумента шаблона черт<value>, чтобы быть<true>. Они не являются достаточными условиями, потому что условия должны быть<true>для всех аргументов и типа возврата<value>для<true>.

Table 1.7. necessary and non sufficient condition on operator argument for value to be true

операторская декларация

<has_op< Arg>>и<has_op< Arg& >>

<has_op< Argconst >>и<has_op< Argconst&>>

<operator>@<(Arg)>

Ложная

Правда

Правда

<operator>@<(Arg const)>

Ложная

Правда

Правда

<operator>@<(Arg &)>

Ложная

Правда

Ложная

<operator>@<(Arg const&)>

Ложная

Правда

Правда


Table 1.8. necessary and non sufficient condition on operator return type for value to be true

операторская декларация

<has_op< ...,void >>

<has_op< ...,Ret >>

<has_op< ...,Ret const>>

<has_op< ...,Ret &>>

<voidoperator>@<(...)>

Правда

Ложная

Ложная

Ложная

Ложная

<Retoperator>@<(...)>

Ложная

Правда

Правда

Ложная

Правда

<Retconst operator>@<(...)>

Ложная

Правда

Правда

Ложная

Правда

<Ret& operator>@<(...)>

Ложная

Правда

Правда

Правда

Правда

<Retconst &operator>@<(...)>

Ложная

Правда

Правда

Ложная

Правда


Implementation

Реализация состоит только из файлов заголовка. В первую очередь следует включить следующие заголовки:

#include <boost/type_traits/has_operator.hpp>

или

#include <boost/type_traits/has_op.hpp>

где<op>- текстовое имя, выбранное для разыскиваемого оператора. Первый метод включает в себя все характеристики оператора.

Все признаки реализованы одинаково с использованием препроцессорных макросов, чтобы избежать дублирования кода. Основные файлы находятся в<boost/type_traits/detail>:<has_binary_operator.hpp>,<has_prefix_operator.hpp>и<has_postfix_operator.hpp>. Пример префикса<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

Limitation
  • Требуется компилятор с работающей SFINAE.
Known issues
  • Эти признаки не могут определить, являются ли операторы общедоступными или нет: если оператор определен как частный член типа<T>, то инстанциация соответствующего признака вызовет ошибку компилятора. По этой причине эти признаки не могут быть использованы для определения того, имеет ли тип публичного оператора или нет.<
    structA{private:Aoperator-();};
    boost::has_unary_minus<A>::value;// error: A::operator-() is private
    
    >
  • Существует проблема, если оператор существует только для типаAиBявляется конвертируемым вA. В этом случае компилятор сообщит о двусмысленной перегрузке, поскольку как существующий оператор, так и тот, который мы предоставляем (с аргументом типаany), нуждаются в преобразовании типа, так что ни один из них не является предпочтительным.
    structA{};
    voidoperator-(constA&);
    structB{operatorA();};
    boost::has_unary_minus<A>::value;// this is fine
    boost::has_unary_minus<B>::value;// error: ambiguous overload between
                                     // operator-(const any&) and
                                     // operator-(const A&)
                                     // both need type conversion
    
    structB{};
    structA{A(constB&){}};
    voidoperator-(constA&);
    boost::has_unary_minus<A>::value;// this is fine
    boost::has_unary_minus<B>::value;// error: ambiguous overload between
                                     // operator-(const any&) and
                                     // operator-(const A&)
                                     // both need type conversion
    
  • Существует проблема при применении этих черт к классам шаблонов. Если оператор определен, но не связывается с заданным типом шаблона, он все равно обнаруживается чертой, которая возвращается<true>вместо<false>. Это относится, в частности, к контейнерам стандартной библиотеки и<operator==>. Пример:<
    #include<boost/type_traits/has_equal_to.hpp>
    #include<iostream>
    template<classT>
    structcontains{Tdata;};
    template<classT>
    booloperator==(constcontains<T>&lhs,constcontains<T>&rhs){
    	returnf(lhs.data,rhs.data);
    }
    classbad{};
    classgood{};
    boolf(constgood&,constgood&){}
    intmain(){
    	std::cout<<std::boolalpha;
    	// works fine for contains<good>
    	std::cout<<boost::has_equal_to<contains<good>>::value<<'\n';// true
    	contains<good>g;
    	g==g;// ok
    	// does not work for contains<bad>
    	std::cout<<boost::has_equal_to<contains<bad>>::value<<'\n';// true, should be false
    	contains<bad>b;
    	b==b;// compile time error
    	return0;
    }
    
    >
  • <volatile>квалификация не обрабатывается должным образом и приведет к неопределенному поведению.
Acknowledgments

Фредерик Брон очень благодарен многим людям из списка рассылки за их добрую помощь и терпение. В частности, весьма полезными для осуществления были следующие лица: Эдвард Динер, Эрик Ниблер, Джеффри Ли Хеллрунг (младший), Роберт Стюарт, Роман Перепелица, Стивен Ватанабе, Висенте Ботет.


PrevUpHomeNext

Статья Operator Type Traits раздела Chapter 1. Boost.TypeTraits Type Traits that Describe the Properties of a Type может быть полезна для разработчиков на c++ и boost.




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.



:: Главная :: Type Traits that Describe the Properties of a Type ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-05-19 18:18:50/0.0121910572052/0