Как использовать вложенные шаблоны в C ++? - PullRequest
4 голосов
/ 06 июля 2019

Я новичок в C ++ и делаю класс в нем сейчас. В качестве домашней работы мы должны написать функцию, которая принимает любой контейнер в качестве входных данных, но не использует итераторы. Поэтому нам не разрешено просто передавать std::begin(container) и std::end(container) в качестве аргументов. Мы должны передать контейнеры в качестве ссылок. Я взял на себя следующее объявление функции:

template <typename T, typename U, typename V, template <typename a, typename b> typename container>
void doStuff(container<T, U>& first, container<T, V>& second)
{
    // the code
}

Он принимает два контейнера (или любой шаблонный тип, который использует два аргумента шаблона). Второй аргумент шаблона в container отличается тем, что в массивах V может представлять размер массива, и я хочу иметь возможность принимать два массива разных размеров.

Пример:

std::array<bool, 4> a1 = { true, false, false, false };
std::array<bool, 1> a2 = { false };

К сожалению, этот пример не работает. Ошибка говорит о том, что я doStuff не принимает аргументы этих типов. Это почему?

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

1 Ответ

3 голосов
/ 06 июля 2019

Если вы хотите поддерживать как можно больше типов контейнеров, вам следует использовать наименее ограничивающее определение:

template <typename FirstContainer, typename SecondContainer>
void doStuff(FirstContainer& first, SecondContainer& second)
{
}

Это связано с тем, что типы контейнеров бывают разных вкусов, например, std::array - это шаблон, принимающий тип значения и статический размер в качестве аргумента, std::vector - это шаблон, принимающий тип значения и распределитель, некоторые пользовательские StringList могут вообще не быть шаблоном.

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

template <typename FirstContainer, typename SecondContainer>
int sum(const FirstContainer& first, const SecondContainer& second)
{
    int result = 0;
    for (auto value : first)
        result += value;
    for (auto value : second)
        result += value;
    return result;
}

Эта функция отлично работает с любыми типами значений, которые могут быть добавлены к целому числу. Если его нельзя добавить (например, std::string), это в конечном итоге приведет к ошибке компиляции.

(Обратите внимание, что эту функцию можно написать еще более обобщенно с автоматически выводимым типом суммы вместо int)

Если вам не хватает этих «неявных требований», вы можете добавить явные проверки, используя static_assert:

template <typename FirstContainer, typename SecondContainer>
int sum(const FirstContainer& first, const SecondContainer& second)
{
    int result = 0;
    for (auto value : first)
    {
        static_assert(std::is_same_v<int, decltype(value)>, "FirstContainer does not hold integers");
        result += value;
    }
    for (auto value : second)
    {
        static_assert(std::is_same_v<int, decltype(value)>, "SecondContainer does not hold integers");
        result += value;
    }
    return result;
}

Теперь ваша функция принимает только контейнеры, содержащие простой int, ничего больше.

Вы также можете использовать std::enable_if, чтобы полностью «отключить» вашу функцию для неподдерживаемых контейнеров:

template <typename FirstContainer, typename SecondContainer>
auto sum(const FirstContainer& first, const SecondContainer& second)
    -> std::enable_if_t<
        std::is_same_v<typename FirstContainer::value_type, int> &&
        std::is_same_v<typename SecondContainer::value_type, int>,
        int
    >
{
    int result = 0;
    for (auto value : first)
        result += value;
    for (auto value : second)
        result += value;
    return result;
}

Теперь ваша функция ограничена контейнерами с вложенным typedef value_type для типа int.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...