【问题标题】:How to determine if a type is dereferenceable in C++03?如何确定类型在 C++03 中是否可取消引用?
【发布时间】:2014-01-17 00:31:05
【问题描述】:

C++03 中,如何确定类型 T 是否可取消引用?
我的意思是,我如何静态确定*t 是否是t 类型T 的有效表达式?

我的尝试:

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
}

它在 GCC 上工作(打印 1010),但在 VC++ (1110) 和 Clang (1111) 上崩溃和烧毁。

【问题讨论】:

  • 我不会对 VC++ 进行模板元编程的能力抱太大希望。至于clang,当foovoid时,它认为&amp;*foo是可以的,即使没有任何模板。
  • @n.m.: 呃,&amp;*foo 有什么关系呢?我这里没有任何地址...
  • 我说的是 clang 对取消引用 void* 的看法,而不是代码中的任何内容。
  • @n.m.:但我的意思是当foovoid* 时,clang 肯定不认为sizeof(*foo) 没问题,对吧?我不确定我是否看到这两个问题之间的任何关系...
  • @n.m.:你怎么能把foo 声明为void 开头呢?我以为你的意思是 void*

标签: c++ dereference


【解决方案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 >
{ };

我已经在 msvs 2008 中测试过了

【讨论】:

    【解决方案2】:

    这是 clang 中的一个错误。有人应该提交报告。

    Clang 实现了对 void 指针进行指针运算的旧 gcc 扩展。这包括能够取消引用 void 指针,并采用 sizeof(void)。 Clang 这样做是因为人们过去常常抱怨旧代码可以用 gcc 编译,但不能用 clang。因此,clang 维护人员继续实施此扩展。这里的问题是多方面的:

    1. 使用 gcc,扩展始终与 C 编译器静默工作,但对 C++ 编译器产生警告。使用 clang,如果使用扩展,C 和 C++ 编译器默认都是静默的。
    2. 对于 g++,sizeof(void) 在模板中使用时会产生替换失败,尽管只是一个警告。使用 clang++,它不会。
    3. 您可以在 clang++ 中使用 -Wpointer-arith -Werror 禁用扩展,但这会导致 sizeof(void) 成为编译错误而不是替换失败。

    我不能保证 VC++。

    【讨论】:

    • 我想这解释了发生了什么(这对我来说很好知道),但它并没有告诉我如何实际做我所要求的......:\
    • 我会用一个示例实现来更新答案,敬请期待。
    • 嗯,这比我预期的要棘手。显然,clang 的粗糙边缘比我在 5 分钟的休息时间所能处理的要多。
    • 是的,在我把它贴在这里之前,我花了几个小时使用各种编译器......
    猜你喜欢
    • 1970-01-01
    • 2011-05-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多