我有这样的功能(已给出,不应修改)。
void readData(int &ID, void*&data, bool &mybool) {
if(mybool)
{
std::string a = "bla";
std::string* ptrToString = &a;
data = ptrToString;
}
else
{
int b = 9;
int* ptrToint = &b;
data = ptrToint;
}
}
因此,我想循环使用此函数,并将返回的函数参数保存在向量中(每次迭代)。 为此,我编写了以下结构:
template<typename T>
struct dataStruct {
int id;
T** data; //I first has void** data, but would not be better to
// have the type? instead of converting myData back
// to void* ?
bool mybool;
};
我的main.cpp看起来像这样:
int main()
{
void* myData = nullptr;
std::vector<dataStruct> vec; // this line also doesn't compile. it need the typename
bool bb = false;
for(int id = 1 ; id < 5; id++) {
if (id%2) { bb = true; }
readData(id, myData, bb); //after this line myData point to a string
vec.push_back(id, &myData<?>); //how can I set the template param to be the type myData point to?
}
}
或者有没有模板的更好方法吗?我用的是c ++ 11(我不能用c ++ 14)
答案 0 :(得分:0)
您说的功能无法修改,即readData()
是应该提醒您的功能!
由于指针设置为局部变量,因此会导致未定义行为。这意味着当函数终止时,这些指针将成为 dangling 指针。
答案 1 :(得分:0)
让我们暂时忽略readData
函数的恶作剧,前提是它只是为了举例(在您的实际用例中不会产生UB)。
您不能直接在std::vector
中存储具有不同(静态)类型的值。值得注意的是,dataStruct<int>
和dataStruct<std::string>
是完全无关的类型,您不能按原样存储在相同的向量中。
您的问题归结为“我拥有以类型不安全的方式提供给我的数据,并希望最终获得对它的类型安全的访问”。解决方案是创建一个将类型不安全的数据解析为的数据结构。例如,似乎您在示例数据中加入了int
和std::string
对的结构(请注意,您的id%2
并没有这样做,因为其他情况丢失了,并且布尔值再也不会设置为false,但是我想您想让它交替显示。
因此,让我们将那堆void*
转换为结构化数据:
std::pair<int, std::string> readPair(int pairIndex)
{
void* ptr;
std::pair<int, std::string> ret;
// Copying data here.
readData(2 * pairIndex + 1, ptr, false);
ret.first = *reinterpret_cast<int*>(ptr);
readData(2 * pairIndex + 2, ptr, true);
ret.second = *reinterpret_cast<std::string*>(ptr);
}
void main()
{
std::vector<std::pair<int, std::string>> parsedData;
parsedData.push_back(readPair(0));
parsedData.push_back(readPair(1));
}
(为简洁起见,我从readData()
签名中删除了引用-通过将临时表达式存储在变量中,您将获得相同的效果。)
通常来说:id
与预期数据类型之间的任何关系都应该变成数据结构-否则,只有在知道当前ID和当前ID的情况下,才可以推断数据条目的类型关系,这正是您应该封装在数据结构中的东西。
答案 2 :(得分:0)
您的readData
不是有用的函数。任何尝试使用它产生的东西都会产生不确定的行为。
是的,没有模板就可以大致完成您要的内容。要有意义地做到这一点,您有两种选择。 “老派”方法是将数据存储在带标签的联合中:
struct tagged_data {
enum { T_INT, T_STR } tag;
union {
int x;
char *y;
} data;
};
这使您可以存储字符串或整数,并且可以设置tag
来告诉您特定tagged_data
项包含哪些项。然后,(至关重要的)将字符串存储到其中时,将动态分配它指向的数据,因此它将保持有效,直到您明确释放数据为止。
不幸的是,(至少在有内存的情况下)C ++ 11不支持在联合中存储非POD类型,因此,如果采用这种方式,则必须如上所述使用char *
,而不是实际的std::string
。
消除(大部分)这些限制的一种方法是使用基于继承的模型:
class Data {
public:
virtual ~Data() { }
};
class StringData : public Data {
std::string content;
public:
StringData(std::string const &init) : content(init) {}
};
class IntData : public Data {
int content;
public:
IntData(std::string const &init) : content(init) {}
};
这有点不完整,但是我认为可能足以给出一般的想法-您将有一个指向基类的指针数组(或向量)。要插入数据,您需要创建一个StringData
或IntData
对象(动态分配),然后将其地址存储到Data *
的集合中。当您需要找回一个时,可以使用dynamic_cast
(除其他事项外)来确定它是从哪一个开始的,然后安全地返回该类型。一切都有些丑陋,但确实有效。
即使在C ++ 11中,您也可以使用基于模板的解决方案。例如,Boost::variant
可以很好地完成这项工作。这将提供重载的构造函数和值语义,因此您可以执行以下操作:
boost::variant<int, std::string> some_object("input string");
换句话说,如果花费时间和精力完成上面概述的基于继承的代码,这将是很不错的选择,除了它明显更干净外,因为它摆脱了存储指针的要求对于基类,请使用dynamic_cast
检索正确类型的对象,依此类推。简而言之,这是解决该问题的正确方法(除非/除非您可以升级到较新的编译器,否则请使用std::variant
)。
答案 3 :(得分:-2)
除了注释/回复中描述的给定代码中的问题外。 我正在尝试回答您的问题
vec.push_back(id, &myData<?>); //how can I set the template param to be the type myData point to?
在您需要按照以下说明修改vec定义
vector<dataStruct<void>> vec;
现在您可以在vector中简单推送元素
vec.push_back({id, &mydata, bb});
我试图修改您的代码,使其可以正常工作
#include<iostream>
#include<vector>
using namespace std;
template<typename T>
struct dataStruct
{
int id;
T** data;
bool mybool;
};
void readData(int &ID, void*& data, bool& mybool)
{
if (mybool)
{
data = new string("bla");
}
else
{
int b = 0;
data = &b;
}
}
int main ()
{
void* mydata = nullptr;
vector<dataStruct<void>> vec;
bool bb = false;
for (int id = 0; id < 5; id++)
{
if (id%2) bb = true;
readData(id, mydata, bb);
vec.push_back({id, &mydata, bb});
}
}