Подтвердить что ты не робот

Специализация шаблона с 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 не имеют ничего с указателями/ссылками?

  • Почему параметры шаблона шаблона не могут быть типа класса?

4b9b3361

Ответ 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.