C ++: Учитывая тип указателя, как можно восстановить тип Pointee? - PullRequest
2 голосов
/ 13 января 2012

Дано:

struct Field
{
  template<class T> T Value() const
  {
    // ???
  }
  template<class S> S SimpleValue() const
  {
    return *reinterpret_cast<const S *>(GetVoidPointerToTheActualValue());
  }
  template<class P> const P *PointerValue() const
  {
    return reinterpret_cast<const P *>(GetVoidPointerToTheActualValue());
  }
};

Как реализовать метод Field::Value<T>(), чтобы компилятор автоматически отправлял:

  • Field::PointerValue<P>, если T действительноP*
  • Field::SimpleValue<S> в противном случае

Кроме того, гарантируется, что T не является ни ссылкой, ни указателем на указатель.

Спасибо.

РЕДАКТИРОВАТЬ

@ Grizzly - я попробовал ваше предложение, к сожалению, оно не сработало во время компиляции Value<LPCWSTR>():

1>  playmssqlce.cpp
1>c:\dev\internal\playmssqlce\playmssqlce.cpp(75): error C2668: 'sqlserver::Field::Value' : ambiguous call to overloaded function
1>          c:\dev\internal\playmssqlce\sqlserverfield.h(19): could be 'std::tr1::enable_if<_Test,_Type> sqlserver::Field::Value<LPCWSTR>(void) const'
1>          with
1>          [
1>              _Test=false,
1>              _Type=LPCWSTR
1>          ]
1>          c:\dev\internal\playmssqlce\sqlserverfield.h(18): or       'std::tr1::enable_if<_Test,_Type> sqlserver::Field::Value<LPCWSTR>(void) const'
1>          with
1>          [
1>              _Test=true,
1>              _Type=LPCWSTR
1>          ]
1>          while trying to match the argument list '(void)'

Мне непонятно почему, потому что ваш совет кажется правильным.Кстати, я использую Visual Studio 2010.

EDIT2

После исправления глупой ошибки у меня все еще есть проблемы.Итак, вот что у меня есть:

struct Field
{
  template<class T> typename enable_if<is_pointer<T>::value, T>::type Value() const { return PointerValue(); }
  template<class T> typename enable_if<!is_pointer<T>::value, T>::type Value() const { return SimpleValue(); }
  template<class T> T SimpleValue() const         { return *reinterpret_cast<const T *>(GetVoidPointerToTheActualValue()); }
  template<class T> const T *PointerValue() const { return reinterpret_cast<const T *>(GetVoidPointerToTheActualValue()); }
};

Я пытаюсь скомпилировать f.Value<const wchar_t *>(), но получаю это:

1>  playmssqlce.cpp
1>c:\dev\internal\playmssqlce\sqlserverfield.h(18): error C2783: 'const T *sqlserver::Field::PointerValue(void) const' : could not deduce template argument for 'T'
1>          c:\dev\internal\playmssqlce\sqlserverfield.h(42) : see declaration of 'sqlserver::Field::PointerValue'
1>          c:\dev\internal\playmssqlce\playmssqlce.cpp(75) : see reference to function template instantiation 'const wchar_t *sqlserver::Field::Value<const wchar_t*>(void) const' being compiled

Что я делаю не так сейчас?

Спасибо.

РЕДАКТИРОВАТЬ3

Глупый меня.Заметил изменение Гризли:

template<class T> typename enable_if<is_pointer<T>::value, T>::type Value() const { return PointerValue<typename std::remove_pointer<T>::type>(); }
template<class T> typename enable_if<!is_pointer<T>::value, T>::type Value() const { return SimpleValue<T>(); }

Работает сейчас.

Ответы [ 2 ]

3 голосов
/ 13 января 2012

Вы можете использовать enable_if:

struct Field {
  /*Other methods of Field*/
  template<class T> 
  typename std::enable_if<std::is_pointer<T>::value, T>::type Value() const {
    return this->PointerValue<typename std::remove_pointer<T>::type>();
  }
  template<class T> 
  typename  std::enable_if<!std::is_pointer<T>::value, T>::type Value() const {
    return this->SimpleValue<T>();
  }

};

Конечно, std::enable_if, std::is_pointer<T> и std::remove_pointer<T> доступны только при наличии C++11.Если вы этого не сделаете, вы можете использовать std::tr1::is_pointer или boost::is_pointer вместе с boost::enable_if (или boost::enable_if_c) или с надписью enable_if (смотрите здесь , чтобы узнать, как это сделать,это довольно тривиально).remove_pointer также доступен как std::tr1::remove_pointer и boost::remove_pointer.

Однако, в зависимости от того, что вы хотите, он все равно может не делать то, что вы хотите, поскольку, как я написал, вам нужно передать const P* в Value(), поскольку именно это возвращает PointerValue().Если вы хотите передать P* и получить const P* обратно, вы можете изменить его на следующее:

typename std::enable_if<
    std::is_pointer<T>::value, typename 
    std::add_const<typename 
        std::remove_pointer<T>::type
    >::type*
>::type Value() const;  

Еще раз используйте std::tr1::add_const или boost::add_const, если у вас нет c ++11

1 голос
/ 13 января 2012

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

Шаблоны функций не поддерживают частичную специализацию: решение было бы использовать шаблон класса помощника для выполнения операций:

template < typename T >
struct getValue
{
    static T apply() { default behavior }
};

template < typename T>
struct getValue< T * >
{
    static T * apply() { behavior for pointer types }
};

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

Другое дело, что тип возвращаемого значения Field::value зависит от параметра шаблона. Чтобы определить правильный тип возвращаемого значения, хорошим решением будет наличие typedef в классе помощника, который можно получить при объявлении Field::value.

Вот полный код этого решения:

#include <iostream>

namespace detail { template < typename T > struct getValue; }

class Field
{
  public:
    void * getVoidPointerToTheActualValue() const;

    template< class T >
    typename detail::getValue< T >::result_type value() const;

  private:
    void * actualValue_;
};

namespace detail {

template < typename T >
struct getValue
{
    typedef T result_type;

    static result_type apply( Field const & f )
    {
        std::cout << "simpleValue" << '\n';
        return *reinterpret_cast< const T * >( 
                f.getVoidPointerToTheActualValue() );
    }
};

template < typename T >
struct getValue< T * >
{
    typedef T const * result_type;

    static result_type apply( Field const & f )
    {
        std::cout << "pointerValue" << '\n';
        return reinterpret_cast< const T * >(
                f.getVoidPointerToTheActualValue() );
    }
};

} //namespace detail


void * Field::getVoidPointerToTheActualValue() const
{
    return actualValue_;
}

template< class T >
typename detail::getValue< T >::result_type Field::value() const
{
    return detail::getValue< T >::apply( *this );
}


int main()
{
    Field f;
    f.value< int >();   //prints "simpleValue"
    f.value< int * >(); //prints "pointerValue"
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...