2011-12-13 65 views
5

一个typedef在C++中,我想有一个类,其构造函数如下:如何检查如果一个类型是INT

class A { 
    explicit A(A* other) { ... } 
    explicit A(intptr_t other) { ... } 
}; 

这样做的问题是,如果用户与初始化

A a(0); 

然后,在一个64位的系统上,编译器会抱怨它不知道0应该被转换为A*还是intptr_t,这足够公平。因为我想这个简单的符号来上班,我添加了下面的构造:

explicit A(int a) { assert(a==0); ... } 

的说法是,因为这是唯一的整数,这是有道理的。现在,问题出现在32位系统中,其中intptr_t实际上是...... int!所以现在,系统抱怨有两个构造函数采用相同的参数类型(这同样足够公平)。

所以我的问题是:是否有与预处理的方式来检测intptr_t实际上是int,在这种情况下,不编译int构造。或者,还有另一种方法可以使A a(0)符号有效,而无需添加构造函数int,但是不会删除两个第一个构造函数中的任何一个(并且不会使它们隐含)。

+0

你不能删除A(A *其他)的构造函数,并用A(A和其他)替换。对我来说这似乎更自然(假设你正在复制其他而不是链接)。 –

回答

3

喜欢的东西

#if INTPTR_MAX == INT_MAX 

可能做的伎俩,但它仍然会造成如此,其中longint是相同的大小,ptrint_tlong一个typedef。 另一种可能性(但不知道是否可以使用它) 将使用uintptr_t,而不是intptr_t

除此之外:预处理器不知道类型,所以问题 不能在那里解决。你必须使用某种元编程技巧:使int构造函数成为一个模板,使用 boost::enable_if只有在参数的类型为int时才激活该模板。 如果ptrint_tint,那么激活的函数将永远不会使用 ,因为它将永远不会比具有相同签名的非模板函数 更好匹配。如果ptrint_t不是int,那么当参数的类型为 int时, 模板实例化将更好地匹配。 (请注意,我从来没有尝试过这个自己:这听起来好像它应该 是可能的,但我没那么熟悉boost::enable_if到 肯定)

+0

谢谢,最后,用'uintptr_t'解决问题! – PierreBdR

+0

@PierreBdR最简单的解决方案是最好的:-)! –

1

你为什么不简单implemment一个无参数作为other的构造函数是0?如果由于某种原因,你不想,我建议使用类型特征,只要你有机会获得C++编译器11或升压:

class A { 
public: 
    explicit A(A* other) { ... } 
    explicit A(intptr_t other) { ... } 

    template <class T> 
    explicit A(T other) 
    { 
     static_assert(std::is_convertible<T, intptr_t>::value, "Could not convert value to intptr_t"); 
     static_assert(std::is_integral<T>::value, "Argument must be integral"); 
     intptr_t p = other; 
     ... 
    } 
}; 

你可以摆脱静态断言和类型checkings的,但然后,而不是编译错误,你会得到一个警告(取决于你的警戒线,甚至有可能变成一个错误),当你做到以下几点:

A a(0.0f); 
+0

这是可能的,但有两个缺点:它很复杂,任何使用错误类型的构造函数都会导致一个不明显的错误。 – PierreBdR

+0

@PierreBdR你究竟在这里发现了些什么?静态断言是自我解释性的。它们产生的错误信息也是相同的。 “参数必须是不可分割的”真的不明显吗? – gwiazdorrr

+0

这更多的是使用模板函数解决首先与模板无关的问题。无论如何,正如你所看到的,我最喜欢的解决方案是从'intptr_t'改为'uintptr_t',这是你会承认的,更简单。 – PierreBdR

-1

有另一种方式,使A a(0)符号有效

只需引入一个template构造函数。

class A { 
public: 
    template<typename T> 
    explicit A (T t) { assert(t==0); } // explicit matters ? 

    explicit A(A* other) { ... } 
    explicit A(intptr_t other) { ... } 
}; 

这将解决您的32位和64位问题!

+1

这是如何解决问题的?访问错误而不是模棱两可的错误? – gwiazdorrr

+0

@gwiazdorrr,我正在修改帖子。查看更新后的答案。我以为OP想限制'0'。然而,有人提到它是'assert()',这意味着另一种方式。我纠正了它。 – iammilind

+0

为什么downvote? – iammilind

-1

你也能传递一个参数,它决定了一个被称为:

struct TakePtr{}; 
struct TakeInt{}; 

class A { 
    A(A* other, const TakePtr&) { ... } 
    A(intptr_t other, const TakeInt&) { ... } 
}; 

这种方式可以确保其构造函数被调用:

A a2(0, TakeInt());  // calls the 2nd constructor, taking int 
A a1(&a2, TakePtr()); // calls the 1st constructor, taking a pointer 
+1

这不是完成任务的好方法。它引入了不必要的[可读性代码气味](http://en.wikipedia.org/wiki/Code_smell#Common_code_smells)。它可以以更简单的方式完成。 – iammilind

+0

@iammilind那么,那个清单中的哪一项是上述代码打破?还是更多的项目?上面的代码将阻止任何转换,并使用特定的构造函数来创建一个对象。 –

+0

'或者更多项目';是的,这是不必要的。你的代码将完成任务,但同时,编码器需要遵守传递适当类型的规则;从而失去其抽象。另外,这种bug可以悄无声息地被忽视:'A a2(0,TakePtr()); // oops应该是TakeInt()' – iammilind

0

我想最简单的事情是声明全部6个构造函数(int,long,long long和它们的无符号变体),而不是使用intptr_t。

+0

那么为什么不更简单的'模板'解决方案? – iammilind

+2

我认为这是过度杀伤... – PierreBdR

+0

这是一个简单而有效的解决方案,你说的问题 –

相关问题