【问题标题】:Accessor function member for variadic templated struct可变参数模板结构的访问器函数成员
【发布时间】:2021-05-23 19:22:08
【问题描述】:

作为练习,我正在虚幻引擎 4 中设计一个面向目标的行动计划器。我正在尝试设置我自己的结构,我可以使用这些结构来动态描述世界状态,使用由个人组成的可变长度数据列表变量类型。我认为可变参数模板结构可能是一种有趣的方式来做到这一点,但是在实现值访问器时我遇到了问题。

我希望它工作,以便我可以在运行时实例化这些结构,在 FFact<T...> 的通用参数中使用 FDataKeys<T>。然后我想要一个函数GetValue<S>(string Key),它将递归地检查数据键,直到找到匹配项。我将实现一个标志参数来指示搜索的成功或失败,但是我的临时代码中出现了一个编译错误,这对我来说没有意义。

我知道我可以设置一个带有静态函数的辅助类来执行此操作,但我很好奇为什么这不起作用以及如果可能的话我将如何以这种方式实现它。

错误信息:

38:42:警告:ISO C++ 禁止在参数声明中使用“auto”[-Wpedantic] 在 'S FFact::GetValue(const string&) [with S = char; T = std::basic_string; R = {}; std::string = std::basic_string]':

50:45: 从 'S FFact::GetValue(const string&) [with S = char; T = 布尔值; R = {std::basic_string}; std::string = std::basic_string]'

50:45: 来自 'S FFact::GetValue(const string&) [with S = char; T = 字符; R = {bool, std::basic_string}; std::string = std::basic_string]'

67:37:从这里需要

46:25:错误:无法将 'std::basic_string' 转换为 'char' 作为回报 在成员函数 'S FFact::GetValue(const string&) [with S = char; T = std::basic_string; R = {}; std::string = std::basic_string]':

52:5:警告:控制到达非 void 函数的结尾 [-Wreturn-type]

代码:

// FFact struct experiments
#include <iostream>
#include <string>
#include <vector>
using namespace std;

// Key-Value data struct
template<typename T>
struct FDataKey
{
    FDataKey(string KeyStr, T Data)
    {
        Key = KeyStr;
        Value = Data;
    }
    
    string Key;
    T Value;
};

// Basic struct to enable generic recursion
template<typename ... T>
struct FFact
{
    // GetValue's bottom of the barrel
    // ToDo: Implement check that Key was not found
    template<typename S>
    S GetValue(string Key)
    {
        S defaultValue;
        return defaultValue;
    }
};

// Variadic templated container meant for FDataKey<> types only
template<typename T, typename ... R>
struct FFact<T, R...>
{
    // Constructors
    FFact(){}
    FFact(const FDataKey<T>& FirstValue, const auto& ...Rest) : Data(FirstValue), Remainder(Rest...)
    {}
    
    // Recursively check Data members for FDataKeys with matching keys
    template<typename S>
    S GetValue(const string& Key)
    {
        if(Data.Key == Key)
        {
            return Data.Value;
        }
        else
        {
            return Remainder.GetValue<S>(Key);
        }
    }
    
    // Member variables
    FDataKey<T> Data;
    FFact<R...> Remainder; 
};

// Run tests
int main() 
{
  // Setup testing Fact
  FFact<char, bool, string> f = FFact<char,bool,string>(
      FDataKey<char>("Initial", 'w'),
      FDataKey<bool>("Cake",false),
      FDataKey<string>("Marco","Polo")
  );
  
  cout << f.GetValue<char>("Initial") << endl;
  cout << f.GetValue<bool>("Cake") << endl;
  cout << f.GetValue<string>("Marco") << endl;
}

【问题讨论】:

  • return Data.Value; - 你在每个递归调用中都执行return Data.Value;,即使你返回一个S。前任。在GetValue&lt;char&gt;。 C++ 是静态类型的,每个Data.Value 都必须转换为char
  • 我想这让我很困惑。因为 Data.Value 在 Key 匹配之前不应返回。因此,唯一的类型转换应该是 S 到 typeof(Data.Value),只有在调用中传递了不正确的泛型类型时才会抛出错误。
  • should not be returned 不会返回,但静态所有路径都必须有效。就像int func() { if (0) { return std::string{}; } return 5; } 不会编译,无论字符串是“从不返回”。

标签: c++ variadic-templates


【解决方案1】:

你去,它编译:

// FFact struct experiments
#include <iostream>
#include <string>
#include <vector>
#include <stdexcept>
#include <type_traits>
using namespace std;

// Key-Value data struct
template<typename T>
struct FDataKey {
    FDataKey(string KeyStr, T Data) {
        Key = KeyStr;
        Value = Data;
    }    
    string Key;
    T Value;
};

// Basic struct to enable generic recursion
template<typename ... T>
struct FFact {
    FFact(){}
    // GetValue's bottom of the barrel
    // ToDo: Implement check that Key was not found
    template<typename S>
    S GetValue(string Key) {
        throw std::runtime_error("key not found");
        S defaultValue;
        return defaultValue;
    }
};

// Variadic templated container meant for FDataKey<> types only
template<typename T, typename ...R>
struct FFact<T, R...> {
    FFact(){}
    // No need to use auto...
    FFact(const FDataKey<T>& FirstValue, const FDataKey<R>&... Rest) : 
    Data(FirstValue), Remainder(Rest...)
    {}
    
    template<typename S>
    S GetValue(const string& Key) {
        if (Data.Key == Key) {
            // you have to statically check 
            // if Data.Value can be converted to S
            // if it can't, then... it can't.
            if constexpr (std::is_convertible<T, S>::value) {
                return Data.Value;
            } else {
                throw std::runtime_error("T is not convertiable to S");
            }
        } else {
            // This is the first time I used this syntax, didn't knew about it.
            return Remainder.template GetValue<S>(Key);
        }
    }
    // Member variables
    FDataKey<T> Data;
    FFact<R...> Remainder; 
};

// Run tests
int main() 
{
  // Setup testing Fact
  FFact<char, bool, string> f = FFact<char,bool,string>(
      FDataKey<char>("Initial", 'w'),
      FDataKey<bool>("Cake",false),
      FDataKey<string>("Marco","Polo")
  );
  
  cout << f.GetValue<char>("Initial") << endl;
  cout << f.GetValue<bool>("Cake") << endl;
  cout << f.GetValue<string>("Marco") << endl;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-22
    • 1970-01-01
    相关资源
    最近更新 更多