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

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


person Brent    schedule 17.01.2013    source источник
comment
Если вам это действительно интересно, прочтите книгу «Метапрограммирование шаблонов С++» основных авторов Boost MPL, в ней объясняются мотивы, стоящие за всеми подобными вещами. И это справедливо не только для C++11, это стало стандартом в C++11 с std::integral_constant.   -  person Mikael Persson    schedule 18.01.2013


Ответы (2)


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

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

Эта мотивация по-прежнему применима в C++11.

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

person K-ballo    schedule 17.01.2013
comment
С++у? Я думал, что это C++1y? - person Andrew Tomazos; 18.01.2013
comment
Не могли бы вы показать код, демонстрирующий проблему, а затем показать рабочую версию? - person Brent; 02.09.2018

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

Ответ К-Балло великолепен.

Хотя есть еще кое-что, что я считаю важным. Интегральные константные типы полезны не только как параметры шаблона, они могут быть полезны как аргументы функций и типы возвращаемых функций (в моих примерах используются типы 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 и структуру, содержащую два int) и проверяют "значение" с помощью sizeof. В С++ 11 функции могут возвращать true_type и false_type, что гораздо более выразительно, например. черта, которая проверяет «есть ли у этого типа член с именем foo?» может заставить функцию, указывающую на положительный результат, возвращать true_type, а функцию, указывающую на отрицательный результат, возвращать false_type, что может быть яснее этого?

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

person Jonathan Wakely    schedule 18.01.2013