How to determine if a type is dereferenceable in C++03?

浪尽此生 提交于 2019-12-07 00:34:29

问题


In C++03, how do I determine if a type T is dereferenceable?
By which I mean, how do I statically determine if *t would be a valid expression for t of type T?

My attempt:

template<bool B, class T = void> struct enable_if { };
template<class T> struct enable_if<true, T> { typedef T type; };

unsigned char (&helper(void const *))[2];
template<class T>
typename enable_if<
    !!sizeof(**static_cast<T *>(NULL)),
    unsigned char
>::type helper(T *);

template<class T>
struct is_dereferenceable
{ static bool const value = sizeof(helper(static_cast<T *>(NULL))) == 1; };

struct Test
{
    int *operator *();
    void operator *() const;
private:
    Test(Test const &);
};

int main()
{
    std::cout << is_dereferenceable<int *>::value;       // should be true
    std::cout << is_dereferenceable<void *>::value;      // should be false
    std::cout << is_dereferenceable<Test>::value;        // should be true
    std::cout << is_dereferenceable<Test const>::value;  // should be false
}

It works on GCC (prints 1010) but crashes and burns on VC++ (1110) and Clang (1111).


回答1:


#include <boost\type_traits\remove_cv.hpp>
#include <boost\type_traits\is_same.hpp>
#include <boost\type_traits\remove_pointer.hpp>
#include <boost\type_traits\is_arithmetic.hpp>
#include <boost\utility\enable_if.hpp>

namespace detail
{
    struct tag 
    { 
        template < typename _T > 
        tag(const _T &); 
    };

    // This operator will be used if there is no 'real' operator
    tag operator*(const tag &);

    // This is need in case of operator * return type is void
    tag operator,(tag, int);  

    unsigned char (&helper(tag))[2];

    template < typename _T >
    unsigned char helper(const _T &);

    template < typename _T, typename _Enable = void >
    struct traits
    {
        static const bool value = (sizeof(helper(((**static_cast <_T*>(NULL)), 0))) == 1);
    };

    // specialization for void pointers
    template < typename _T >
    struct traits < _T,
        typename boost::enable_if < typename boost::is_same < typename boost::remove_cv < typename boost::remove_pointer < _T > ::type > ::type, void > > ::type >
    {
        static const bool value = false;
    };

    // specialization for arithmetic types
    template < typename _T >
    struct traits < _T,
        typename boost::enable_if < typename boost::is_arithmetic < typename boost::remove_cv < _T > ::type > > ::type >
    {
        static const bool value = false;
    };
}

template < typename _T > 
struct is_dereferenceable :
    public detail::traits < _T >
{ };

I have tested it in msvs 2008




回答2:


It's a bug in clang. Somebody should file a report.

Clang implements the old gcc extension of doing pointer arithmetic on void pointers. This includes being able to dereference a void pointer, and to take sizeof(void). Clang does so because people used to complain about old code compilable with gcc but not clang. So clang maintainers went ahead and implemented this extension. The problems here are many-fold:

  1. With gcc, the extension always worked silently with the C compiler, but produced a warning with the C++ compiler. With clang, both C and C++ compilers are silent by default if the extension is used.
  2. With g++, sizeof(void) produces a substitution failure when used in a template, despite being only a warning. With clang++, it does not.
  3. You can disable the extension in clang++ with -Wpointer-arith -Werror, but this leads to sizeof(void) being a compilation error instead of a substitution failure.

I cannot vouch for VC++.



来源:https://stackoverflow.com/questions/20834168/how-to-determine-if-a-type-is-dereferenceable-in-c03

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!