Когда я пытаюсь использовать float
в качестве параметра шаблона, компилятор требует этого кода, пока int
работает нормально.
Это потому, что я не могу использовать float
в качестве параметра шаблона?
#include<iostream>
using namespace std;
template <class T, T defaultValue>
class GenericClass
{
private:
T value;
public:
GenericClass()
{
value = defaultValue;
}
T returnVal()
{
return value;
}
};
int main()
{
GenericClass <int, 10> gcInteger;
GenericClass < float, 4.6f> gcFlaot;
cout << "\n sum of integer is "<<gcInteger.returnVal();
cout << "\n sum of float is "<<gcFlaot.returnVal();
return 0;
}
Ошибка:
main.cpp: In function `int main()':
main.cpp:25: error: `float' is not a valid type for a template constant parameter
main.cpp:25: error: invalid type in declaration before ';' token
main.cpp:28: error: request for member `returnVal' in `gcFlaot',
which is of non-class type `int'
Я читаю «Структуры данных для программистов игр» Рона Пентона, автор пропускает float
, но когда я пробую, кажется, что он не компилируется.
float
качестве параметра шаблона не типа ? В какой главе это?Ответы:
Текущий стандарт C ++ не позволяет
float
использовать литералы (т.е. действительные числа) или символьные строки в качестве параметров, не относящихся к типу . Вы можете, конечно , использоватьfloat
иchar *
тип в качестве обычных аргументов.Возможно, автор использует компилятор, который не соответствует текущему стандарту?
источник
template<char ...cs>
, то строковый литерал может быть преобразован в такой пакет во время компиляции. Вот демо на идеоне . (Демо - C ++ 14, но его легко перенести обратно на C ++ 11 -std::integer_sequence
это единственная трудность)char &*
в качестве параметра шаблона, если определите литерал где-то еще. Очень хорошо работает как обходной путь.ПРОСТОЙ ОТВЕТ
Стандарт не допускает использование плавающих точек в качестве аргументов шаблона, не являющихся типом , о чем можно прочитать в следующем разделе стандарта C ++ 11;
Но .. но .. ПОЧЕМУ !?
Вероятно, это связано с тем, что вычисления с плавающей запятой не могут быть представлены точно. Если бы это было разрешено, это могло бы / привело бы к ошибочному / странному поведению при выполнении таких действий;
Мы хотели вызвать одну и ту же функцию дважды, но это может быть не так, поскольку представление двух вычислений с плавающей запятой не обязательно будет точно таким же.
Как мне представить значения с плавающей запятой в качестве аргументов шаблона?
С
C++11
его помощью вы можете написать несколько довольно сложных константных выражений ( constexpr ), которые будут вычислять числитель / знаменатель времени компиляции плавающего значения, а затем передавать эти два как отдельные целочисленные аргументы.Не забудьте определить какой-то порог, чтобы значения с плавающей запятой, близкие друг к другу, давали один и тот же числитель / знаменатель , в противном случае это бессмысленно, поскольку тогда будет получен тот же результат, который ранее упоминался как причина не разрешать значения с плавающей запятой как не типовые аргументы шаблона .
источник
<ratio>
описывается в §20.10 как «Рациональная арифметика времени компиляции». Что прямо к вашему примеру.<ratio>
?12345 * 12345
такое? (Это действительно позволяютint
параметры шаблона , даже если он не определяет ширину подписанных межд или же это выражение UB.)Просто чтобы указать одну из причин, почему это ограничение (по крайней мере, в текущем стандарте).
При сопоставлении специализаций шаблона компилятор сопоставляет аргументы шаблона, включая аргументы, не относящиеся к типу.
По своей природе значения с плавающей запятой неточны, и их реализация не определяется стандартом C ++. В результате трудно решить, действительно ли совпадают два аргумента не типа с плавающей запятой:
Эти выражения не обязательно образуют один и тот же «битовый шаблон», и поэтому невозможно гарантировать, что они используют одну и ту же специализацию - без специальной формулировки, чтобы охватить это.
источник
==
оператора :-) Мы уже принимаем эту неточность во время выполнения, почему бы и во время компиляции?Действительно, вы не можете использовать литералы с плавающей запятой в качестве параметров шаблона. См. Раздел 14.1 («Параметр шаблона без типа должен иметь один из следующих (необязательно cv-квалифицированных) типов ...») стандарта.
Вы можете использовать ссылку на float в качестве параметра шаблона:
источник
Оберните параметр (ы) в их собственный класс как constexprs. По сути, это похоже на трейт, поскольку он параметризует класс с помощью набора чисел с плавающей запятой.
а затем создайте шаблон, взяв тип класса в качестве параметра
а потом используйте это так ...
Это позволяет компилятору гарантировать, что для каждого экземпляра шаблона с одним и тем же пакетом параметров создается только один экземпляр кода. Это решает все проблемы, и вы можете использовать числа с плавающей запятой и удвоения как constexpr внутри шаблонного класса.
источник
Если вы согласны иметь фиксированное значение по умолчанию для каждого типа, вы можете создать тип, чтобы определить его как константу и при необходимости специализировать.
Если у вас есть C ++ 11, вы можете использовать constexpr при определении значения по умолчанию. В C ++ 14 MyTypeDefault может быть переменной шаблона, которая синтаксически немного чище.
источник
Другие ответы дают веские причины, по которым вам, вероятно, не нужны параметры шаблона с плавающей запятой, но реальный тормоз IMO заключается в том, что равенство с использованием '==' и побитовое равенство не одно и то же:
-0.0 == 0.0
, но0.0
и-0.0
не побитовое равенствоNAN != NAN
Ни один из видов равенства не является хорошим признаком равенства типов: конечно, пункт 2 делает
==
недопустимым использование для определения равенства типов. Можно использовать побитовое равенство вместо этого, ноx != y
не означает , чтоMyClass<x>
иMyClass<y>
различные типы (по 2), которое было бы довольно странно.источник
Всегда можно подделать ...
Ссылка: http://code-slim-jim.blogspot.jp/2013/06/c11-no-floats-in-templates-wtf.html
источник
float
! = Рациональное число. Это две очень разные идеи. Одно вычисляется с помощью мантиссы и показателя степени, а другое является рациональным - не каждое значение, представимое рациональным числом, может быть представлено с помощью afloat
.float
- определенно рациональное число, но естьfloat
s, которые не могут быть представлены как отношения двухint
s. Мантисса представляет собой целое число, а показатель степени 2 ^ - целое числоЕсли вам не нужно, чтобы значение double было константой времени компиляции, вы можете передать его как указатель:
источник
Начиная с C ++ 20 это возможно .
Это также дает ответ на исходный вопрос:
Потому что в стандарт это еще никто не реализовал. Нет фундаментальной причины.
В C ++ 20 параметрами шаблона, не являющимися типами, теперь могут быть числа с плавающей запятой и даже объекты класса.
Существуют некоторые требования к объектам класса (они должны быть буквального типа ) и выполнение некоторых других требований для исключения патологических случаев, таких как пользовательский оператор == ( Подробности ).
Мы даже можем использовать
auto
Обратите внимание, что GCC 9 (и 10) реализует параметры шаблона, не относящиеся к типу, но еще не для float .
источник
Если вы хотите представить только фиксированную точность, вы можете использовать подобный прием для преобразования параметра float в int.
Например, массив с коэффициентом роста 1,75 может быть создан следующим образом при условии 2-значной точности (разделить на 100).
Если вам не нравится представление 1,75 как 175 в списке аргументов шаблона, вы всегда можете заключить его в какой-нибудь макрос.
источник
...::Factor = _Factor_/100.0;
иначе это будет целочисленное деление.