我正在实现一个变体类(不使用boost),我想知道你如何处理你存储任何字符串,整数或双精度的情况,并通过ToString自动将其转换为所需的类型(),ToInt()或ToDouble()。
例如,
Variant a = 7;
cout << "The answer is" + a.ToString() << endl; // should print "The answer is 7"
a = "7.4";
double &b = a.ToDouble();
b += 1;
cout << a.ToString() << endl; // should print 8.4
ToXXX
函数应该返回您要转换为的类型的引用。现在,我有代码,它可以返回与最初分配给它的相同类型(Variant a = Int(7); a.ToInt()
工作),并在分配的类型与您要转换的类型不同时引发异常。
抱歉,使用提升不是一种选择。
答案 0 :(得分:11)
#include <string>
#include <iostream>
class Variant{
public:
Variant(){
data.type = UNKOWN;
data.intVal = 0;
}
Variant(int v){
data.type = INT;
data.intVal = v;
}
Variant(double v){
data.type = DOUBLE;
data.realVal = v;
}
Variant(std::string v){
data.type = STRING;
data.strVal = new std::string(v);
}
//the missing copy constructor
Variant(Variant const& other)
{
*this = other;// redirect to the copy assignment
}
~Variant(){
if(STRING == data.type){
delete data.strVal;
}
}
Variant& operator = (const Variant& other){
if(this != &other)
{
if(STRING == data.type){
delete data.strVal;
}
switch(other.data.type){
case STRING:{
data.strVal = new std::string(*(other.data.strVal));
data.type = STRING;
break;
}
default:{
memcpy(this, &other, sizeof(Variant));
break;
}
}
}
return *this;
}
Variant& operator = (int newVal){
if(STRING == data.type){
delete data.strVal;
}
data.type = INT;
data.intVal= newVal;
return *this;
}
Variant& operator = (double newVal){
if(STRING == data.type){
delete data.strVal;
}
data.type = DOUBLE;
data.realVal= newVal;
return *this;
}
Variant& operator = (std::string& newVal){
if(STRING == data.type){
delete data.strVal;
}
data.type = STRING;
data.strVal= new std::string(newVal);
return *this;
}
operator int&() {
if(INT == data.type)
{
return data.intVal;
}
//do type conversion if you like
throw std::runtime_error("bad cast");
}
operator double&() {
if(DOUBLE == data.type){
return data.realVal;
}
//do type conversion if you like
throw std::runtime_error("bad cast");
}
operator std::string&() {
if(STRING == data.type){
return *data.strVal;
}
throw std::runtime_error("bad cast");
}
private:
enum Type{
UNKOWN=0,
INT,
DOUBLE,
STRING
};
struct{
Type type;
union
{
int intVal;
double realVal;
std::string* strVal;
};
} data;
};
int main(){
Variant v("this is string");//string
v=1;//int
v=1.0;//double
v=std::string("another string");//
Variant v2; //unkown type
v2=v;//string
std::cout << (std::string&)v2 << std::endl;
return 0;
}
答案 1 :(得分:7)
要实现这样的东西,你需要能够改变存储的类型,因为如果用户通过引用更改变量,他希望更改影响存储的值,所以我写这样的东西:< / p>
class Variant
{
private:
enum StoreType
{
Integer,
Float,
String,
}
store_type;
union
{
int * as_integer;
double * as_double;
std::string * as_string;
}
store_pointer;
// convert to type
void integer_to_double();
void integer_to_string();
void double_to_integer();
void double_to_string();
void string_to_integer();
void string_to_double();
public:
...
int & ToInt()
{
switch (store_type)
{
case Integer: break;
case Double: double_to_integer(); break;
case String: string_to_integer(); break;
}
return * as_integer;
}
...
}
答案 2 :(得分:2)
我自己实现了一个简单的变体类(不使用第三方库)。每个ToXxx
函数都包含switch
超过m_type
(枚举,表示当前持有的类型)。对于字符串转换(包括from和to),我使用std::stringstream
。这真是微不足道。非常像 Mooing Duck 建议。
P上。 S.如果打算频繁调用相同值的字符串转换,我会缓存它。
答案 3 :(得分:1)
首先,你绝对需要通过引用返回吗?按值返回可能会更容易,因为您可以随时执行此操作,而无需更改Variant对象的内部状态。
如果您确实需要通过引用返回,那么您需要有一个有效的内存位置来返回引用。 (例如,返回对堆栈对象的引用并不好,因为当ToXXX()方法返回时堆栈对象将消失,并且引用将是对无效内存的引用)
这样做的简单方法是在Variant对象中包含每个类型的(可变?)成员变量,并设置该成员变量的值并根据需要返回对它的引用。当然,这样做的缺点是它使你的Variant对象和所有可能对象的总和一样大,但如果你不太关心内存使用情况,这可能没问题。
如果你也关心最小化Variant对象的内存使用量,那么你可能需要使用一个union(或类似的一个)。 C联合会适用于POD类型,但如果你需要包含非POD类型(例如std :: string对象),它们就不够了。如果需要,可以使用字节缓冲区(足够大以容纳最大可能类型)并在必要时使用placement-new和explicit析构函数调用,但实现起来有点繁琐。
就数据类型的实际转换而言(例如“7” - &gt;(int)7 - &gt;(double)7.0,您只需实现逻辑(可能通过嵌套的switch语句?)为每一对可能的“源”和“目的地”类型做正确的事情。我认为没有任何神奇的方法,除了使用提升功能之外,它已经为你完成了。
答案 4 :(得分:1)
如果您希望能够即时进行转换,这是我能想到的唯一方法。
class Variant {
enum internaltype {stringtype, inttype, doubletype} curtype;
std::string strdata; //can't be in union
union {
int intdata;
double doubledata;
};
public:
Variant() :curtype(inttype) {}
Variant(const std::string& s) : curtype(stringtype), strdata(s) {}
Variant(int i) : curtype(inttype) {intdata = i;}
Variant(double d) : curtype(doubletype) {doubledata = d;}
std::string& ToString() {
std::stringstream ss;
switch(curtype) {
case inttype:
ss << intdata;
ss >> stringdata;
break;
case doubletype:
ss << doubledata;
ss >> stringdata;
break;
}
curtype = stringtype;
return &strdata;
}
int& ToInt() {/*much the same*/}
double& ToDouble() {/*much the same*/}
};
答案 5 :(得分:1)
使用boost::any作为值持有者而不是自定义类,这是一个快速而又脏的实现。正如您所看到的,模板可以帮助您缩短代码量。
ToXXX函数更改存储值的基础类型(除非不需要实际转换),然后返回对它的引用。转换是使用boost::lexical_cast完成的,这可能不完全适合您的目的(如果转换根据lexical_cast非常严格的条件不成功,它将抛出异常)。
#include <boost/any.hpp>
#include <boost/lexical_cast.hpp>
#include <string>
#include <typeinfo>
class Variant
{
boost::any value;
public:
Variant(int n): value(n) {}
Variant(double d): value(d) {}
Variant(const std::string& s): value(s) {}
Variant(const char* s): value(std::string(s)) {} //without this, string literals create ambiguities
int& ToInt() { return convert<int>();}
double& ToDouble() { return convert<double>(); }
std::string& ToString() { return convert<std::string>(); }
private:
template <class T>
T& convert()
{
if (typeid(T) != value.type()) { //otherwise no conversion required
if (typeid(int) == value.type()) {
value = boost::lexical_cast<T>(boost::any_cast<int>(value));
}
else if (typeid(double) == value.type()) {
value = boost::lexical_cast<T>(boost::any_cast<double>(value));
}
else if (typeid(std::string) == value.type()) {
value = boost::lexical_cast<T>(boost::any_cast<std::string>(value));
}
}
return *boost::any_cast<T>(&value);
}
};
#include <iostream>
using namespace std;
int main()
{
Variant a = 7;
cout << "The answer is" + a.ToString() << endl; // should print "The answer is 7"
a = "7.4";
double &b = a.ToDouble();
b += 1;
cout << a.ToString() << endl; // should print 8.4
}
(行动中:http://codepad.org/C3l5AXg3)
使用boost::variant<int, double, std::string>
实现可能更加简单,因为您可以使用模板化operator()
的单个访问者将其拉出来,而不必为每个访问者编写单独的代码路径不同类型。
答案 6 :(得分:1)
只是要知道C ++ 17将实现std::variant来执行此操作。
答案 7 :(得分:0)
根据您的需要,我建议下载http://qt.nokia.com并查看QVariant Class的实现。如果这看起来过于复杂,我会建议这样的事情:
class Variant
{
private:
enum data_type {
...
};
data_type variant_type;
union Data {
char *string;
int integer;
double dbl;
} data;
public:
Variant(const int data);
Variant(const double data);
Variant(const char *data);
// I think that implementation of the constructors should be pretty straight forward.
int ToInt() const;
double ToDouble() const;
std::string ToString() const;
};
从这里开始实施转换应该是直截了当的。
如果您希望它更简单且性能不是主要问题,则可以将存储在变体中的数据转换为字符串,然后在调用To方法时将其转换回来。
class Variant
{
private:
char data_string[16384];
public:
Variant(const int data);
Variant(const double data);
Variant(const char *data);
// I think that implementation of the constructors should be pretty straight forward.
int ToInt() const;
double ToDouble() const;
std::string ToString() const;
};
如果您需要转换功能的实现细节,我可以提供它们,但我认为它们应该非常简单。