Как заставить класс шаблона принимать массив любого размера в качестве аргумента?

#c #templates

Вопрос:

Как я могу создать класс с аргументом шаблона, который принимает массив любого размера? Я хочу передать массив в качестве аргумента шаблона, чтобы я мог хэшировать строку во время компиляции, что-то вроде этого:

 template <typename>
struct CharArrayWrapper
{
    
};

template <typename T, int N>
struct CharArrayWrapper<T[N]>
{
    static constexpr int stringLen() { N - 1; }
};
 

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

1. В современном C вам ничего из этого не понадобится. Просто напишите хэш-функцию, как обычно, нажмите constexpr на нее, и она может сработать из коробки.

Ответ №1:

Мне это кажется довольно уродливым, но, похоже, это работает: https://godbolt.org/z/cd96Pjsdj

 #include <algorithm>
#include <iostream>

template<typename T, std::size_t N>
struct MyArray
{
    explicit MyArray(T (amp;data)[N]) : data_{data} {}
    size_t size() const noexcept { return N; }
    const T* get() const noexcept { return data_; }
private:
    T (amp;data_)[N]{};
};

int main()
{
    const char ac[]{"hey joe"};  // array of chars
    MyArray tac{ac};  // templated array of chars
    std::cout << "tac: " << tac.size() << ", " << tac.get() << "n";

    const int ai[]{1, 2, 3};  // array of ints
    MyArray tai{ai};  // templated array of ints
    std::cout << "tai: " << tai.size() << ", ";
    std::for_each(tai.get(), tai.get()   tai.size(), [](auto i){ std::cout << i << " "; });
}

// Outputs:
//     tac: 8, hey joe
//     tai: 3, 1 2 3
 

Если вам действительно нужна версия, которая делает копию вашего массива в шаблонную структуру: https://godbolt.org/z/rs5zs6Ysr

 #include <algorithm>
#include <iostream>

template<typename T, std::size_t N>
struct MyArray
{
    explicit MyArray(const T (amp;data)[N]) { std::copy(data, data   N, data_); }
    size_t size() const noexcept { return N; }
    const T* get() const noexcept { return data_; }
private:
    T data_[N]{};
};

int main()
{
    char ac[]{"hey joe"};  // array of chars
    MyArray tac{ac};  // templated array of chars
    ac[0] = 'H';
    std::cout << "ac: " << sizeof(ac)/sizeof(char) << ", " << ac << "n";
    std::cout << "tac: " << tac.size() << ", " << tac.get() << "n";


    int ai[]{1, 2, 3};  // array of ints
    MyArray tai{ai};  // templated array of ints
    ai[0] = 10;
    std::cout << "ai: " << tai.size() << ", ";
    std::for_each(ai, ai   sizeof(ai)/sizeof(int), [](auto i){ std::cout << i << " "; });
    std::cout << "n";
    std::cout << "tai: " << tai.size() << ", ";
    std::for_each(tai.get(), tai.get()   tai.size(), [](auto i){ std::cout << i << " "; });
    std::cout << "n";
}

// Outputs:
//     ac: 8, Hey joe
//     tac: 8, hey joe
//     ai: 3, 10 2 3 
//     tai: 3, 1 2 3 
 

Примечание Я добавил const к типу data параметра. В противном случае при передаче массивов const конструктору выводимый тип T будет const препятствовать изменению элемента data_ .


Идея этого решения взята из эффективной современной книги Скотта Мейера по C , Пункт 1 (Вывод типов) и раздел, посвященный аргументам массива; там вы можете увидеть следующий фрагмент кода:

 // return size of an array as a compile-time 
template<typename T, std::size_t N>
constexpr std::size_t arraySize(T (amp;)[N]) noexcept {
    return N;
}
 

Объяснение, также из книги, было бы:

  • Функции могут объявлять параметры, которые являются ссылками на массивы.
  • В этом случае тип, для которого выводится T , является фактическим типом массива.
  • Этот тип включает размер массива, поэтому в моем примере T выводится значение const char [8] (для массива символов ac ), а тип параметра data (ссылка на этот массив) равен const char (amp;)[8] .