c ++:给定指针类型如何恢复指针类型?

时间:2012-01-13 16:32:04

标签: c++ templates

假设:

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

我现在做错了什么?

感谢。

EDIT3

愚蠢的我。注意到Grizzly的变化:

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 个答案:

答案 0 :(得分:3)

您可以使用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_ifstd::is_pointer<T>std::remove_pointer<T>只有C++11才可用。如果不这样做,您可以将std::tr1::is_pointerboost::is_pointerboost::enable_if(或boost::enable_if_c)或自编enable_if一起使用(查看{{3对于如何做到这一点,它是非常微不足道的)。 remove_pointerstd::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;  

如果您没有c ++ 11,请再次使用std::tr1::add_constboost::add_const

答案 1 :(得分:1)

您希望您的函数模板具有两种不同的行为,具体取决于它实例化的参数类型。这需要模板专业化。在这种情况下,由于您希望专门针对所有指针类型,因此您需要的是部分模板特化。

函数模板不支持部分特化:解决方案是使用辅助类模板来执行操作:

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的返回类型取决于模板参数。要确定什么是正确的返回类型,一个好的解决方案是在helper类中有一个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"
}