Почему Boost MPL имеет интегральные константы?

Так как вы можете принимать целочисленные значения в качестве параметров шаблона и выполнять арифметику с ними, что является мотивацией для boost :: mpl :: int_<> а другие интегральные константы? Применяется ли эта мотивация в C ++ 11?

10

Решение

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

Например, рассмотрим метафункцию find это работает с типами и ищет одинаковый тип в последовательности. Если вы хотите использовать его с нетиповые параметры шаблона вам нужно будет переопределить новые алгоритмы «перегрузки», find_c для которого вы должны вручную указать тип интегрального значения. Теперь представьте, что вы хотите, чтобы он работал со смешанным интегралом типы как остальная часть языка, или что вы хотите смешать типы а также не-типы, вы получаете взрыв «перегрузок», которые также сложнее использовать, так как вы должны везде указывать тип каждого нетипового параметра.

Эта мотивация все еще применяется в C ++ 11.

Эта мотивация все еще будет применяться к C ++ у и любая другая версия, если у нас нет какого-то нового правила, которое позволяет преобразование из нетиповые параметры шаблона в тип параметры шаблона. Например, всякий раз, когда вы используете 5 и шаблон запрашивает тип создает его std::integral_constant< int, 5 > вместо.

12

Другие решения

tldr; Кодирование значения как типа позволяет использовать его в гораздо большем количестве мест, чем простое значение. Вы можете перегружать типы, вы не можете перегружать значения.

Ответ K-Ballo великолепен.

Есть что-то еще, что я считаю актуальным. Типы целочисленных констант не только полезны в качестве параметров шаблона, они могут быть полезны в качестве аргументов функций и типов возвращаемых функций (в моих примерах используются типы C ++ 11, но тот же аргумент применяется к типам Boost, предшествующим им):

template<typename R, typename... Args>
std::integral_constant<std::size_t, sizeof...(Args)>
arity(R (*)(Args...))
{ return {}; }

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

Даже с constexpr на языке (что означает, что функция выше может просто return sizeof...(Args); и что целочисленное значение можно было бы использовать во время компиляции) все еще есть хорошие применения для целочисленных константных типов, например, диспетчеризация тегов:

template<typename T>
void frobnicate(T&& t)
{
frob_impl(std::forward<T>(t), std::is_copy_constructible<T>{});
}

это frob_impl функция может быть перегружена на основе integer_constant<bool, b> Тип передается в качестве второго аргумента:

template<typename T>
void frob_impl(T&& t, std::true_type)
{
// do something
}

template<typename T>
void frob_impl(T&& t, std::false_type)
{
// do something else
}

Вы можете попробовать сделать что-то подобное, сделав логическое значение параметром шаблона:

frob_impl<std::is_copy_constructible<T>::value>(std::forward<T>(t));

но невозможно частично специализировать шаблон функции, поэтому вы не можете frob_impl<true, T> а также frob_impl<false, T> делать разные вещи. Перегрузка на тип логической константы позволяет легко делать разные вещи на основе значение черты «является копируемой конструкцией», и тот все еще очень полезен в C ++ 11.

Другое место, где константы полезны, — для реализации признаков с использованием SFINAE. В C ++ 03 традиционным подходом было перегружать функции, которые возвращают два типа с разными размерами (например, int и структура, содержащая два ints) и проверить «значение» с sizeof, В C ++ 11 функции могут возвращать true_type а также false_type что гораздо более выразительно, например, черта, которая проверяет «имеет ли этот тип член с именем foo? «может сделать функцию, указывающую на положительный результат возврата true_type и сделать функцию, указывающую на возврат отрицательного результата false_typeЧто может быть более ясным, чем это?

В качестве стандартного разработчика библиотеки я делаю очень частое использование true_type а также false_typeпотому что многие «вопросы» во время компиляции имеют истинные / ложные ответы, но когда я хочу протестировать что-то, что может иметь более двух разных результатов, я буду использовать другие специализации integral_constant,

13