Член функции-аксессора для вариативной шаблонной структуры

В качестве упражнения я разрабатываю ориентированный на цель планировщик действий в Unreal Engine 4. Я пытаюсь настроить свои собственные структуры, которые я могу использовать для динамического описания состояния мира, используя список данных переменной длины, состоящий из отдельных типов переменных. Я думал, что вариативные шаблонные структуры могут быть интересным способом сделать это, но при реализации метода доступа к значению я столкнулся с проблемами.

Я хочу, чтобы он работал, чтобы я мог создавать экземпляры этих структур во время выполнения, используя FDataKeys<T> в общих параметрах для FFact<T...>. Затем я хочу иметь функцию GetValue<S>(string Key), которая будет рекурсивно проверять ключи данных, пока не будет найдено совпадение. Я буду реализовывать параметр флага, чтобы указать на успех или неудачу поиска, но я получаю ошибку компиляции в моем рабочем коде, которая не имеет для меня смысла.

Я знаю, что могу настроить вспомогательный класс со статической функцией для этого, но мне любопытно, почему это не работает и как я смогу реализовать это таким образом, если это возможно.

Сообщение об ошибке:

38:42: предупреждение: ISO C ++ запрещает использование ‘auto’ в объявлении параметра [-Wpedantic] При создании экземпляра ‘S FFact ‹T, R …› :: GetValue (const string &) [with S = char; Т = std :: basic_string; R = {}; std :: string = std :: basic_string] ‘:

50:45: требуется рекурсивно из ‘S FFact ‹T, R …› :: GetValue (const string &) [with S = char; Т = логический; R = {std :: basic_string ‹char, std :: char_traits, std :: allocator›}; std :: string = std :: basic_string] ‘

50:45: требуется от ‘S FFact ‹T, R …› :: GetValue (const string &) [with S = char; T = char; R = {bool, std :: basic_string ‹char, std :: char_traits, std :: allocator›}; std :: string = std :: basic_string] ‘

67:37: требуется отсюда

46:25: ошибка: невозможно преобразовать ‘std :: basic_string’ в ‘char’ в возвращении В функции-члене ‘S FFact ‹T, R …› :: GetValue (const string &) [with S = char; Т = std :: basic_string; R = {}; std :: string = std :: basic_string] ‘:

52: 5: предупреждение: элемент управления достигает конца непустой функции [-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<char>. C ++ статически типизирован, тогда каждый Data.Value должен быть преобразован в char.   —  person LazarusOfSuburbia    schedule 21.02.2021

См. также:  Явное расщепление массива на указатель

Думаю, вот что меня смущает. Поскольку Data.Value не следует возвращать до тех пор, пока ключ не совпадет. Таким образом, единственным преобразованием типа должно быть преобразование S в typeof(Data.Value), которое должно выдавать ошибку только тогда, когда в вызове передаются неправильные универсальные типы.   —  person LazarusOfSuburbia    schedule 21.02.2021

should not be returned он не будет возвращен, но статически все пути должны быть действительными. Как int func() { if (0) { return std::string{}; } return 5; } не будет компилироваться, независимо от того, что строка никогда не возвращается.   —  person LazarusOfSuburbia    schedule 21.02.2021

Понравилась статья? Поделиться с друзьями:
IT Шеф
Комментарии: 1
  1. LazarusOfSuburbia

    Вот и все, он компилируется:

    // 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;
    }
    
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: