C++ 从 class 中检索多种类型的数据
C++ retrieving multiple types of data from a class
我制作了一个 class 来保存从文件中读取的所有数据。
class Entry
{
std::string key;
std::string value;
std::vector<std::string> arrayString;
std::vector<Entry> arrayEntry;
enum dataValueCheck
{
str,arrStr,arrEnt
};
dataValueCheck dataValue;
public:
Entry(std::string key, std::string value)
:key(key), value(value)
{
dataValue = str;
}
Entry(std::string key, std::vector<std::string> value)
:key(key), arrayString(value)
{
dataValue = arrStr;
}
Entry(std::string key, std::vector<Entry> value)
:key(key), arrayEntry(value)
{
dataValue = arrEnt;
}
std::string getKey()
{
return key;
}
template <typename T>
T getData()
{
switch (dataValue)
{
case Entry::str:
return value;
case Entry::arrStr:
return arrayString;
case Entry::arrayEntry:
return arrayEntry;
default:
break;
}
}
};
这个文件基本上是一个 JSON 文件并且类似于这个结构。
{
Name:Test,
Arr_Item:[1,2,3],
Sub_Item:
{
Item1 : [0,1,2,3],
Item2 : [4,5,6],
}
}
我让它读取数据很好。我遇到困难的地方是使用作为 class 一部分的 getData
函数检索数据。当我尝试使用 template
时,出现“非法大小写”错误。如何从 class 中获取数据?
更新
我有一个 vector
填满了条目 class。当我尝试像这样访问它时:
std::vector<Entry> DB;
std::string test = DB[0].getData<std::string>();
它应该 return“测试”,但我得到了这个错误:
1>C:\Dev\Playground\Playground\src\Main.cpp(454,1): error C2440: 'return': cannot convert from 'std::vector<Entry,std::allocator<Entry>>' to 'std::basic_string<char,std::char_traits<char>,std::allocator<char>>'
1>C:\Dev\Playground\Playground\src\Main.cpp(454,4): message : No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
您的 getData
函数没有处理 switch
语句中的默认情况。您可以 return 诸如 nullptr 之类的东西,或者在
情况下抛出异常
template <typename T = nullptr_t>
T getData()
{
switch (dataValue)
{
// Cases
default:
return nullptr;
}
}
问题是并非所有 return 值都可以有效地转换为任何特定 T
的 T
。
让我们以您发布的示例为例。我们有这个表达式:
DB[0].getData<std::string>();
如果我们用 std::string
代替 T
,这就是 getData()
的样子:
std::string getData()
{
switch (dataValue)
{
case Entry::str:
return value;
case Entry::arrStr:
return arrayString;
case Entry::arrayEntry:
return arrayEntry;
default:
break;
}
}
只有 Entry::str
的情况在这里有效,因为它实际上 return 是 std::string
。其他情况 return 是 std::vector<std::string>
或 std::vector<Entry>
,两者都不能转换为 std::string
。
有很多方法可以解决这个问题,但最好的方法可能是认识到 Entry
基本上是映射到 std::variant
的键。如果 getData()
return 编辑 std::variant
,您将在依赖标准类型时避免许多潜在的陷阱。或者,如果您想坚持当前的方法,您将需要某种方法 return 基于 T
而不是(仅)基于 dataValue
的正确值。也就是说,您需要某种方式来处理 T
与 dataValue
.
设置的期望值不匹配的可能性
如果您删除模板并从 getData
中 return 一个 std::variant
,这一切都会变得非常容易。如果您首先将 Entry
中的值作为 std::variant
存储(您只需要将其中一种类型存储在 Entry
的任何特定实例中,对吧?) .
这是一个完整的例子。请注意,我通过 const 引用将参数传递给构造函数以避免不必要的复制;出于同样的原因,您也可能 return (const) 来自 getKey
和 getData
的引用:
#include <iostream>
#include <variant>
#include <string>
#include <vector>
class Entry
{
using EntryType = std::variant <std::string, std::vector <std::string>, std::vector <Entry>>;
std::string key;
EntryType value;
public:
template <typename T>
Entry(const std::string &key, const T &value) : key(key), value(value) {}
std::string getKey() { return key; }
EntryType getData() { return value; }
};
int main() {
Entry e { "key", "string value" };
std::cout << std::get <std::string> (e.getData ());
}
作为返回变体的替代方法,您可以在 getData
中使用 if constexpr
template <typename T>
T getData()
{
if constexpr(std::is_same_v<T, std::string>) {
return (dataValue == Entry::str) ? value : throw type_error(dataValue, Entry::str);
} else if constexpr(std::is_same_v<T, std::vector<std::string>>) {
return (dataValue == Entry::arrStr) ? arrayString : throw type_error(dataValue, Entry::arrStr);
} else if constexpr(std::is_same_v<T, std::vector<Entry>>) {
return (dataValue == Entry::arrEnt) ? arrayEntry : throw type_error(dataValue, Entry::arrEnt);
} else {
#error Invalid type requested for Entry::getData
}
}
我制作了一个 class 来保存从文件中读取的所有数据。
class Entry
{
std::string key;
std::string value;
std::vector<std::string> arrayString;
std::vector<Entry> arrayEntry;
enum dataValueCheck
{
str,arrStr,arrEnt
};
dataValueCheck dataValue;
public:
Entry(std::string key, std::string value)
:key(key), value(value)
{
dataValue = str;
}
Entry(std::string key, std::vector<std::string> value)
:key(key), arrayString(value)
{
dataValue = arrStr;
}
Entry(std::string key, std::vector<Entry> value)
:key(key), arrayEntry(value)
{
dataValue = arrEnt;
}
std::string getKey()
{
return key;
}
template <typename T>
T getData()
{
switch (dataValue)
{
case Entry::str:
return value;
case Entry::arrStr:
return arrayString;
case Entry::arrayEntry:
return arrayEntry;
default:
break;
}
}
};
这个文件基本上是一个 JSON 文件并且类似于这个结构。
{
Name:Test,
Arr_Item:[1,2,3],
Sub_Item:
{
Item1 : [0,1,2,3],
Item2 : [4,5,6],
}
}
我让它读取数据很好。我遇到困难的地方是使用作为 class 一部分的 getData
函数检索数据。当我尝试使用 template
时,出现“非法大小写”错误。如何从 class 中获取数据?
更新
我有一个 vector
填满了条目 class。当我尝试像这样访问它时:
std::vector<Entry> DB;
std::string test = DB[0].getData<std::string>();
它应该 return“测试”,但我得到了这个错误:
1>C:\Dev\Playground\Playground\src\Main.cpp(454,1): error C2440: 'return': cannot convert from 'std::vector<Entry,std::allocator<Entry>>' to 'std::basic_string<char,std::char_traits<char>,std::allocator<char>>'
1>C:\Dev\Playground\Playground\src\Main.cpp(454,4): message : No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
您的 getData
函数没有处理 switch
语句中的默认情况。您可以 return 诸如 nullptr 之类的东西,或者在
template <typename T = nullptr_t>
T getData()
{
switch (dataValue)
{
// Cases
default:
return nullptr;
}
}
问题是并非所有 return 值都可以有效地转换为任何特定 T
的 T
。
让我们以您发布的示例为例。我们有这个表达式:
DB[0].getData<std::string>();
如果我们用 std::string
代替 T
,这就是 getData()
的样子:
std::string getData()
{
switch (dataValue)
{
case Entry::str:
return value;
case Entry::arrStr:
return arrayString;
case Entry::arrayEntry:
return arrayEntry;
default:
break;
}
}
只有 Entry::str
的情况在这里有效,因为它实际上 return 是 std::string
。其他情况 return 是 std::vector<std::string>
或 std::vector<Entry>
,两者都不能转换为 std::string
。
有很多方法可以解决这个问题,但最好的方法可能是认识到 Entry
基本上是映射到 std::variant
的键。如果 getData()
return 编辑 std::variant
,您将在依赖标准类型时避免许多潜在的陷阱。或者,如果您想坚持当前的方法,您将需要某种方法 return 基于 T
而不是(仅)基于 dataValue
的正确值。也就是说,您需要某种方式来处理 T
与 dataValue
.
如果您删除模板并从 getData
中 return 一个 std::variant
,这一切都会变得非常容易。如果您首先将 Entry
中的值作为 std::variant
存储(您只需要将其中一种类型存储在 Entry
的任何特定实例中,对吧?) .
这是一个完整的例子。请注意,我通过 const 引用将参数传递给构造函数以避免不必要的复制;出于同样的原因,您也可能 return (const) 来自 getKey
和 getData
的引用:
#include <iostream>
#include <variant>
#include <string>
#include <vector>
class Entry
{
using EntryType = std::variant <std::string, std::vector <std::string>, std::vector <Entry>>;
std::string key;
EntryType value;
public:
template <typename T>
Entry(const std::string &key, const T &value) : key(key), value(value) {}
std::string getKey() { return key; }
EntryType getData() { return value; }
};
int main() {
Entry e { "key", "string value" };
std::cout << std::get <std::string> (e.getData ());
}
作为返回变体的替代方法,您可以在 getData
if constexpr
template <typename T>
T getData()
{
if constexpr(std::is_same_v<T, std::string>) {
return (dataValue == Entry::str) ? value : throw type_error(dataValue, Entry::str);
} else if constexpr(std::is_same_v<T, std::vector<std::string>>) {
return (dataValue == Entry::arrStr) ? arrayString : throw type_error(dataValue, Entry::arrStr);
} else if constexpr(std::is_same_v<T, std::vector<Entry>>) {
return (dataValue == Entry::arrEnt) ? arrayEntry : throw type_error(dataValue, Entry::arrEnt);
} else {
#error Invalid type requested for Entry::getData
}
}