Специализация шаблона с float как не тип
Раздел 4.3 С++ Templatesгосударства "Не в состоянии использовать литералы с плавающей запятой (и простые постоянные выражения с плавающей запятой) поскольку аргументы шаблона имеют исторический причины".
Аналогично,
$14.1/7 состояний - "Не-тип параметр шаблона не должен быть объявлено, что имеет плавающую точку, класса или типа void. [Пример:
template<double d> class X; // error
template<double* pd> class Y; // OK
template<double& rd> class Z; // OK"
-
Какова историческая причина, о которой говорится в книге в приведенной выше цитате?
-
Глядя на то, почему Y и Z являются действительными, но не X, представляет собой всю проблему, связанную с тем, что параметры шаблона типа non type не имеют ничего с указателями/ссылками?
-
Почему параметры шаблона шаблона не могут быть типа класса?
Ответы
Ответ 1
Трудно было бы выбрать правильное создание шаблона из-за возможных ошибок округления.
Рассмотрим следующее:
template<float n>
void f(n) {...} //Version 1
template<0.3333>
void f() { ...} // Version 2:Specialization for 0.3333
f(1/3);
→ Какую версию вызывать?
Рассмотрим следующий код:
template <float f> class foo { ... };
foo<1E6 + 1E-6> my_foo;
"Что должен генерировать компилятор? Компилятор должен знать о деталях цели
с плавающей точкой, чтобы иметь возможность запускать экземпляр шаблона.
Это достаточно легко, если компилятор работает на целевой
архитектуры, он может просто выполнить расчет и выяснить ответ, но
если вы выполняете кросс-компиляцию, компилятор должен был бы синтезировать
поведение с плавающей точкой для каждой предполагаемой целевой архитектуры. А также
к счастью Комитет по стандартам решил, что это будет необоснованным. "
Бесстыдно скопировано из здесь.
Почему параметры шаблона шаблона не могут быть типа класса
В соответствии с моим пониманием не-тип paramater не может быть типа класса, потому что может быть более одной реализации класса. Например
template <typename T>
class demo{...};
template <>
class demo<int>{...};
template <typename T, demo d> //which demo?? demo<T> or demo<int>
class Example{...};
Локальные классы не могут использоваться в качестве параметров шаблона, потому что у них нет внешней привязки.
Ответ 2
Числа с плавающей запятой не имеют универсального представления (и некоторые значения даже не могут быть представлены без потерь точности, поскольку они основаны на аппроксимации) и поэтому могут отличаться от платформы к платформе, что приводит к тому, что один и тот же код С++ генерирует разные шаблоны на разных платформах.
(Можно сказать, что С++ поддерживает кросс-компиляцию, не требуя, чтобы компилятор полностью эмулировал арифметику с плавающей запятой целевой машины. Разрешение float или double в качестве параметра шаблона могло бы сделать это недействительным.)
template<float F>
float squared_float()
{
return F * F;
}
Например, squared_float < 1.0 > может быть той же функцией, что и squared_float < 1.00000001 > для некоторых реализаций, тогда как для других они будут представлять собой две различные функции.
A reference to a float
означает, что компилятор может оптимизировать его, поскольку он знает его значение и что он никогда не должен меняться.
Для pointer
, ну его просто другой зависимый от архитектуры (32 бит /64 бит) тип данных, который не имеет ничего общего с float.
Ответ 3
Точная кодировка значений с плавающей запятой более подвержена причудам отдельных процессоров. Например, при оценке предположительно постоянного выражения, должен ли процессор использовать более высокоточные 80-битные регистры процессора и только округлять до 64-бит в конце? Если один из компиляторов говорит "да", а другой нет - шаблон получит два разных экземпляра. Но у некоторого другого компилятора могут быть только 64-битные регистры, и, возможно, разные процессоры могут отличаться значением epsilon. Порядок, в котором какой-то компилятор решает оценить выражение, или была скомпилирована библиотека с использованием библиотеки эмуляции с плавающей запятой и т.д., Может привести к таким неправильным совпадениям. Кроме того, числа с плавающей запятой имеют некоторые странные случаи кросс: положительные и отрицательные 0 и т.д., Для которых должно быть определено поведение.
Эти проблемы могут потенциально укусить в средах, где объекты скомпилированы на разных компьютерах (с разными процессорами, версиями компилятора и флагами и т.д.), но нужно надежно связывать их. Предприятия обычно это делают, и библиотеки с двоичным распределением также сталкиваются с такими проблемами. Компиляторы С++ обычно пытаются использовать некоторый прикладной двоичный интерфейс (ABI), который как можно более согласован в разных версиях и средах, но в настоящее время они не стандартизируют, как рассчитываются параметры с плавающей запятой, и это не очевидно, как они могли бы без eg. ожидая, что все компиляторы будут использовать одну и ту же программную эмуляцию с плавающей запятой для получения значений. Это потребует усилий по координации, и существующие решения эмуляции могут иметь проблемы с лицензированием.
Интересно, что Уолтер Брайт (из Digital Mars) считал, что это все дерьмо и разрешает постоянные с плавающей запятой в D... думаю, он получал некоторый реальный опыт в отношении последствий, которые были бы полезны для сообщества С++, но я рад Недавно я слышал.
Ответ 4
Решение этой проблемы состоит в использовании рациональных чисел. Отправьте два целочисленных непиговых параметра и затем инициализируйте свой float в конструкторе следующим образом:
template<int dNum =1, int dDen = 3>
class myclass {
double d;
myclass: d(dNum/dDen) {}
};
voila, передавая a float
.
Ответ 5
Возможное решение этой проблемы - использовать тип, который имеет постоянное значение, которое является float, а затем использовать этот тип в качестве параметра шаблона. Например, если вы хотите иметь целочисленный многочлен, скажем, и хотите оценить его при некотором значении с плавающей запятой:
template <int a, int b, int c>
class Polynomial {
public:
template <typename t>
static constexpr float eval() {
return a*t::value*t::value + b*t::value + c;
}
};
class THREE_POINT_FIVE {
public:
static constexpr float value = 3.5f;
};
int main() {
constexpr float y = Polynomial<2, 0, 1>::typename eval<THREE_POINT_FIVE>();
std::cout << y << std::endl;
}
Можно также использовать вспомогательные классы, которые позволяют создавать классы поплавков, например, для процентов:
template <unsigned int p>
class PERCENT {
public:
static constexpr float value = p * 0.01f;
};
...
constexpr float y2 = Polynomial<2, 0, 1>::typename eval<PERCENT<43>>
...
Я думаю, это похоже на использование ранее упомянутого std::ratio
.