Проверено статическое приведение по ссылке - PullRequest
5 голосов
/ 29 ноября 2010

Давным-давно я создал следующий шаблон, чтобы я получал подтверждение всякий раз, когда выполняю static_cast, но тип не тот, который я предполагаю:

/// perform a static_cast asserted by a dynamic_cast
template <class Type, class SourceType>
Type static_cast_checked(SourceType item)
{
  Assert(!item || dynamic_cast<Type>(item));
  return static_cast<Type>(item);
}

Сегодня я хотел создать вариант, который бы работал не только с указателями, но и со ссылками:

/// overload for reference
template <class Type, class SourceType>
Type &static_cast_checked(SourceType &item)
{
  Assert(dynamic_cast<Type *>(&item));
  return static_cast<Type>(item);
}

Однако компилятор, похоже, не использует эту перегрузку, когда я приводю ссылку на другую ссылку. Боюсь, я недостаточно разбираюсь в правилах разрешения шаблонов, чтобы понять почему или создать вариант, который работает.

Примечание: я не могу поймать bad_cast exception вместо проверки dynamic_cast<Type *> на NULL, поскольку исключения для этого проекта отключены.

Ответы [ 4 ]

5 голосов
/ 30 ноября 2010

Удалить * и & из возвращаемых типов:

/// perform a static_cast asserted by a dynamic_cast 
template <class Type, class SourceType> 
Type static_cast_checked(SourceType *item) 
{ 
  Assert(!item || dynamic_cast<Type>(item)); 
  return static_cast<Type>(item); 
} 

template <class Type> struct make_pointer
{
    typedef Type *PointerType;
};

template <class Type> struct make_pointer<Type &>
{
    typedef Type *PointerType;
};

/// overload for reference 
template <class Type, class SourceType> 
Type static_cast_checked(SourceType &item) 
{ 
  Assert(dynamic_cast<typename make_pointer<Type>::PointerType>(&item)); 
  return static_cast<Type>(item); 
} 

Затем вы можете использовать желаемый синтаксис:

Derived *d= static_cast_checked<Derived *>(b);
Derived &d= static_cast_checked<Derived &>(b);

EDIT: добавлено преобразование типа указателя.

2 голосов
/ 29 ноября 2010

Это работает:

/// perform a static_cast asserted by a dynamic_cast
template <class Type, class SourceType>
Type* static_cast_checked(SourceType *item)
{
  Assert(!item || dynamic_cast<Type*>(item));
  return static_cast<Type*>(item);
}

/// overload for reference
template <class Type, class SourceType>
Type &static_cast_checked(SourceType &item)
{
  Assert(dynamic_cast<Type *>(&item));
  return static_cast<Type&>(item);
}

Используйте это так:

Dervied d;
Base* pbase = static_cast_checked<Base>(&d);
Base& rbase = static_cast_checked<Base>(d);

Это решение основано на перегрузке шаблонов функций. Ваше решение не работает, потому что ваш первый шаблон слишком общий, он уже включает в себя вашу вторую функцию. Обратите внимание, что для шаблонов функций нет специализации! Вы можете специализировать только шаблоны классов.

1 голос
/ 30 ноября 2010
#include <boost/type_traits/add_pointer.hpp>

template <class Type, class SourceType>
Type static_cast_checked(SourceType *item)
{
  assert(!item || dynamic_cast<Type>(item));
  return static_cast<Type>(item);
}

template <typename Type,  class SourceType>
Type static_cast_checked(SourceType &item)
{
   typedef typename boost::add_pointer<Type>::type TypePtr;
   assert(dynamic_cast< TypePtr >(&item));
   return static_cast<Type>(item);
}

Эта реализация работает как static_cast или dynamic_cast из библиотеки std:

    Base &b    = static_cast_checked<Base&>( a);
    Base* bPtr = static_cast_checked<Base*>(&a);
0 голосов
/ 29 ноября 2010

Попробуйте следующее:

template<class Type, class SourceType>
Type* static_cast_checked(SourceType* item)
{
    // ... 
}

template<class Type, class SourceType>
Type& static_cast_checked(SourceType& item)
{
    // ... 
}

Тогда каждый указатель будет использовать первый вариант, а каждая ссылка будет использовать второй по правилам частичной специализации (IIRC).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...