Есть ли способ предотвратить создание другого оператора if для разных типов?

#c #templates

#c #шаблоны

Вопрос:

Есть ли способ сократить все операторы if constexpr, предполагая, что поиск буферов — это одно и то же поведение?

 template <typename T>
struct Buffer
{};

struct BufferManager
{
    Buffer<int> intBuffers[8];
    Buffer<float> floatBuffers[8];
    Buffer<char> charBuffers[8];

    template <typename T>
    static void uploadBuffer(int size, const T* data)
    {
        Buffer<T>* buffer = findSuitableBuffer<T>(size);


    }

    template<typename T>
    static Buffer <T>* findSuitableBuffer(int size)
    {
        if constexpr (std::is_same_v<T, int>)
        {
            // SEARCH intBuffers FOR A SUITABLE BUFFER
        }
        else if constexpr (std::is_same_v<T, float>)
        {
            // SEARCH floatBuffers FOR A SUITABLE BUFFER
        }
        else if constexpr (std::is_same_v<T, char>)
        {
            // AND SO ON
        } // DO I HAVE TO ADD ONE OF THESE EACH TIME I WANT TO ADD A TYPE?

        Buffer<T> g{};
        return amp;g;
    }

};
  

Комментарии:

1. Знаете ли вы, что вы возвращаете временный адрес в конце функции? Кроме того, вы хотите выполнить поиск нестатического члена внутри статической функции. Это может привести к ошибке IMO.

2. Ах да, я забыл удалить, я избавлялся от ошибок компилятора, спасибо.

3. Если у вас есть if else construct, который предназначен для проверки типа чего-либо, у вас есть главный кандидат на полиморфную (не шаблонную) базу с (возможно) шаблонным классом, производным от этой базы и переопределяющим унаследованные виртуальные функции-члены.

4. Вы можете использовать std::tuple для хранения всех ваших <T>Buffers элементов данных, а затем вы можете использовать std::get<T>(<your tuple member>) . Но, вероятно, вам нужно будет немного изменить свою архитектуру … чтобы все было правильно.

5. Вам действительно нужны 3 отдельных буфера для начала?

Ответ №1:

Один из способов избавиться от if constexpr — это сделать специализации findSuitableBuffer , которые возвращают правильный массив буферов. Другой способ — findSuitableBuffer полностью пропустить и сохранить буферы в кортеже.

Пример:

 #include <cstddef>
#include <tuple>

struct BufferManager {
    static constexpr size_t kBuffers = 8;

    std::tuple<Buffer<int>[kBuffers],
               Buffer<float>[kBuffers],
               Buffer<char>[kBuffers]> Buffers;

    template <typename T>
    void uploadBuffer(const T* data, size_t size) {
        autoamp; buffers = std::get<Buffer<T>[kBuffers]>(Buffers);

        // SEARCH buffers FOR A SUITABLE BUFFER
        for(size_t i = 0; i < kBuffers;   i) {
            if(buffers[i].has_room_for(size)) {
                // ...
            }
        }        
    }
};
  

Ответ №2:

Не изменяя способ хранения ваших буферов, вы можете написать a findSuitableBuffer , который принимает несколько разнородно типизированных буферов с помощью шаблона переменной рекурсивной функции…

 template <typename T>
Buffer<T>amp; findSuitableBufferImpl(size_t size) {
    throw std::runtime_error("not found");
}

template <typename T, typename U, typename...Ts, size_t I, size_t...Is>
Buffer<T>amp; findSuitableBufferImpl(size_t size, Buffer<U>(amp;buffer)[I], Buffer<Ts>(amp;...buffers)[Is]) {
    if constexpr (std::is_same_v<T, U>) {
        for (size_t i = 0; i < I; i  ) {
            if (/* test buffer[i] */) {
                return buffer[i];
            }
        }
    }
    return findSuitableBufferImpl<T>(size, buffers...);
}

template <typename T>
Buffer<T>amp; findSuitableBuffer(size_t size) {
    // Add types here
    return findSuitableBufferImpl<T>(size, intBuffers, floatBuffers, charBuffers);
}
  

Демонстрация: https://godbolt.org/z/aYY4Ye


И, если бы вы могли использовать кортеж для хранения этих буферов, потребовалась бы небольшая корректировка:

 template <typename... Ts>
struct BufferSet : std::tuple<Buffer<Ts>[8]...> {
    using std::tuple<Buffer<Ts>[8]...>::tuple;
};

template <typename T, typename... Ts, size_t... Is>
constexpr Buffer<T> findSuitableBufferImpl(size_t size, BufferSet<Ts...>amp; buffers, std::index_sequence<Is...>) {
    return findSuitableBufferImpl<T>(size, std::get<Is>(buffers)...);
}

template <typename T, typename... Ts>
constexpr Buffer<T> findSuitableBuffer(size_t size, BufferSet<Ts...>amp; buffers) {
    return findSuitableBufferImpl<T>(size, buffers, std::index_sequence_for<Ts...>{});
}

// usage
BufferSet<int, double, char> buffers;
autoamp; buffer = findSuitableBuffer<double>(4, buffers);
  

Демонстрация: https://godbolt.org/z/v7PzGc

Комментарии:

1. Вау, этот рекурсивный пример потрясающий.