基元数据类型的包装器类
Wrapper Classes for Primitive Data Types
在设计解决方案时,有时为基元数据类型提供包装类可能很方便。考虑一个表示数值的类,无论是double
、float
还是int
。
class Number {
private:
double val;
public:
Number(int n) : val(n) { }
Number(float n) : val(n) { }
Number(double n) : val(n) { }
// Assume copy constructors and assignment operators exist
Number& add(const Number& other) {
val += other.val;
return *this;
}
int to_int() const { return (int) val; }
float to_float() const { return (float) val; }
double to_double() const { return val; }
};
现在假设我有一个这样的函数:
void advanced_increment(Number& n) {
n.add(1);
}
我会这样使用这个函数:
Number n(2);
advanced_increment(n); // n = 3
这听起来很简单。但是如果函数是这样的呢?
void primitive_increment(int& n) {
++n;
}
请注意,增量只是一个示例。假设该函数将对基元数据类型执行更复杂的操作,而它们也应该能够在没有任何问题的情况下对Number
类型执行这些操作。
我该如何像以前一样使用该函数?如:
Number n(2);
primitive_increment(n);
如何使我的Number
类与primitive_increment
兼容?如何为基元数据类型创建一个包装类,使其在需要这些数据类型的任何地方都兼容?
到目前为止,我只找到了两个解决方案。一种是创建一个像double& Number::get_value()
这样的函数,然后像primitive_increment(n.get_value());
一样使用它。第二种解决方案是创建隐式转换方法,如Number::operator int&()
;但这些可能会导致许多不明确的调用,并使代码变得混乱。
我想知道是否有其他解决方案可以实现这些类型的包装器并保留其原始功能。
更新:
为了进一步澄清,在实际项目中,这里的意图是在设计此类解决方案时,使所有数据类型都派生自一个基类,该基类通常被称为Object
。限制条件是不应使用外部库。因此,如果我有一个具有指向类型Object
的指针的容器,那么它应该能够保存任何任意值,无论是否为基元,并执行Object
上允许的任何基元操作。我希望这能更好地解释它。
C++11具有显式运算符重载。
struct silly_wrapper {
int foo;
explicit operator int&() { return foo; }
};
void primitive_increment(int& x) { ++x; }
int main()
{
silly_wrapper x;
primitive_increment(x); // works
x += 1; // doesn't work - can't implicitly cast
}
class Number {
enum ValType {DoubleType, IntType} CurType;
union {
double DoubleVal;
int IntVal;
};
public:
Number(int n) : IntVal(n), CurType(int) { }
Number(float n) : DoubleVal(n), CurType(DoubleType) { }
Number(double n) : DoubleVal(n), CurType(DoubleType) { }
// Assume copy constructors and assignment operators exist
Number& add(const Number& other) {
switch(CurType) {
case DoubleType: DoubleVal += other.to_double(); break;
case IntType: IntVal+= other.to_int(); break;
}
return *this;
}
int& to_int() {
switch(CurType) {
case DoubleType: IntVal = DoubleVal; CurType = IntType; break;
//case IntType: DoubleVal = IntVal; CurType = DoubleType; break;
}
return IntVal;
}
const int to_int() const {
switch(CurType) {
case DoubleType: return (int)DoubleVal;
case IntType: return (int)IntVal;
}
}
const float to_float() const {
switch(CurType) {
case DoubleType: return (float)DoubleVal;
case IntType: return (float)IntVal;
}
}
double& to_double() {
switch(CurType) {
//case DoubleType: IntVal = DoubleVal; CurType = IntType; break;
case IntType: DoubleVal = IntVal; CurType = DoubleType; break;
}
return DoubleVal;
}
const double to_double() const {
switch(CurType) {
case DoubleType: return (double)DoubleVal;
case IntType: return (double)IntVal;
}
}
};
void primitive_increment(int& n) {
++n;
}
int main() {
Number pi(3.1415);
primitive_increment(pi.to_int());
//pi now is 4
return 0;
}
我承认这很尴尬,不是理想的情况,但它解决了给定的问题。
而不是将其提供给primitive_increment
。您应该重载Number
类的++运算符,并以这种方式递增。
Number& operator++() { ++val; return *this;}
Number& operator+=(const Number& rhs) { val += rhs.Val; return *this;}
Number operator+(const Number& rhs) { Number t(*this); t+=rhs; return t;}
请参阅:C和C++中的运算符
如果您的Number
类没有实现int
的子集,则无法实现。例如,如果Number
类包含值INT_MAX
,并且可以保存值INT_MAX+1
,则会给出错误的结果。如果您的Number
类为int
的子集建模,那么转换为int
并返回当然是一种选择。
除此之外,您唯一的机会就是重写函数以接受Number
对象。理想情况下,将它作为一个模板,这样它就可以与int
和Number
(以及任何其他当前或未来提供类似int
接口的类(一起工作。
将转换运算符设为私有运算符,并在其内部使用友元函数进行转换。
class silly_wrapper {
private:
int foo;
float bar;
operator int&() { return foo; }
template <typename T>
friend void primitive_increment(T& x) { ++static_cast<int&>(x); }
};
int main()
{
silly_wrapper x;
primitive_increment(x); // works
int i;
primitive_increment(i); // works
int& r = static_cast<int&>(x); // can't convert - operator is private
}
这里有一个我刚刚想到的更奇怪的答案:
class Number;
template<class par, class base>
class NumberProxy {
base Val;
par* parent;
NumberProxy(par* p, base v) :parent(p), Val(v) {}
NumberProxy(const NumberProxy& rhs) :parent(rhs.parent), Val(rhs.Val) {}
~NumberProxy() { *parent = Val; }
NumberProxy& operator=(const NumberProxy& rhs) {Val = rhs.Val; return *this}
operator base& {return Val;}
};
class Number {
private:
double val;
public:
Number(int n) : val(n) { }
Number(float n) : val(n) { }
Number(double n) : val(n) { }
// Assume copy constructors and assignment operators exist
int to_int() const { return (int) val; }
float to_float() const { return (float) val; }
double to_double() const { return val; }
NumberProxy<Number,int> to_int() { return NumberProxy<Number,int>(this,val); }
NumberProxy<Number,float> to_float() { return NumberProxy<Number,float>(this,val); }
NumberProxy<Number,double> to_double() { return NumberProxy<Number,double>(this,val); }
};
void primitive_increment(int& n) {
++n;
}
int main() {
Number pi(3.1415);
primitive_increment(pi.to_int());
//pi now is 4
return 0;
}
Number.to_int()
返回一个NumberProxy<int>
,它可以隐式转换为函数操作的int&
。当函数和表达式完成时,临时NumberProxy<int>
被销毁,它的析构函数用更新的值更新它的父Number
。这增加了只需要对Number
类进行微小修改的便利性。
显然,这里有一些危险,如果你在同一个语句中调用to_N()
两次,两个int&s不会同步,或者如果有人使用int&超过声明末尾。
模板化的免费函数如何:
class IncTagIntegral{};
class IncTagNonintegral{};
template <bool> struct IncTag { typedef IncTagNonintegral type; }
template <> struct IncTag<true> { typedef IncTagIntegral type; }
template <typename T> void inc_impl(T & x, IncTagIntegral)
{
++x;
}
template <typename T> void inc_impl(T & x, IncTagNonintegral)
{
x += T(1);
}
template <typename T> void primitive_increment(T & x)
{
inc_impl<T>(x, typename IncTag<std::is_integral<T>::value>::type());
}
template <> void primitive_increment(Number & x)
{
// whatever
}
这种方法可以推广到需要应用于现有类型和您自己的类型的其他函数。
这是另一个长镜头,这次使用类型擦除:
struct TEBase
{
virtual void inc() = 0;
}
struct any
{
template <typename T> any(const T &);
void inc() { impl->inc(); }
private:
TEBase * impl;
};
template <typename T> struct TEImpl : public TEBase
{
virtual void inc() { /* implement */ }
// ...
}; // and provide specializations!
template <typename T> any::any<T>(const T & t) : impl(new TEImpl<T>(t)) { }
关键是,您可以通过专门化的方式提供TEImpl<T>::inc()
的不同具体实现,但您可以将a.inc()
用于任何类型为any
的对象a
。您可以基于这个想法构建额外的免费函数包装器,如void inc(any & a) { a.inc(); }
。
- 防止主数据类型C++的隐式转换
- 处理小于cpu数据总线的数据类型.(c++转换为机器代码)
- 在C++中打印指向不同基元数据类型的指针的内存地址
- C++浮点数据类型和字符串数据类型无法子到模板函数中
- 如何计算数据类型的范围,例如int
- C++中数据类型修饰符的顺序
- C++LinkedList问题.数据类型之间存在冲突?没有匹配的构造函数
- 特定数据类型的模板类
- 具有多个模板的模板函数,用于特定数据类型(如字符串)?
- 有没有办法提示用户使用哪种数据类型作为模板 c++
- int数据类型的指针指向的是什么,如果是一个类的私有数据成员,我们创建了该类的两个对象?
- 时间复杂度 当具有复合数据类型(如元组或对)时?
- 如何获取C++字符数据类型的地址
- 将复杂的非基元C++数据类型转换为 Erlang/Elixir 格式,以使用 NIF 导出方法
- 构造智能点数据类型以及普通数据类型的通用方法
- 如何使映射键具有两种不同的数据类型?
- 数据类型"struct seq<0, 1, 2>{}"含义是什么?
- 如何在Swig中为非基元数据类型创建包装器
- 基元数据类型的包装器类
- 用SWIG包装第三方类/数据类型