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 值都可以有效地转换为任何特定 TT

让我们以您发布的示例为例。我们有这个表达式:

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 的正确值。也就是说,您需要某种方式来处理 TdataValue.

设置的期望值不匹配的可能性

如果您删除模板并从 getData 中 return 一个 std::variant,这一切都会变得非常容易。如果您首先将 Entry 中的值作为 std::variant 存储(您只需要将其中一种类型存储在 Entry 的任何特定实例中,对吧?) .

这是一个完整的例子。请注意,我通过 const 引用将参数传递给构造函数以避免不必要的复制;出于同样的原因,您也可能 return (const) 来自 getKeygetData 的引用:

#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
    }
}