Специализация шаблона с плавающим типом - PullRequest
11 голосов
/ 27 сентября 2010

Раздел 4.3 из Шаблоны C ++ заявляет «Не в состоянии использовать литералы с плавающей точкой (и просто постоянные выражения с плавающей точкой) в качестве аргументов шаблона имеет исторический причины. "

Аналогично,

14,1 долл. США / 7 штатов - "Нетип шаблон-параметр не должен быть объявлено с плавающей точкой, класс или тип void. [Пример:

template<double d> class X; // error
template<double* pd> class Y; // OK
template<double& rd> class Z; // OK"
  1. Какова историческая причина, о которой говорится в книге в цитате выше?

  2. Глядя на то, почему Y и Z действительны, но не X, является ли вся проблема, связанная с тем, чтобы не шаблонные параметры плавающего типа могли что-либо делать с указателями / ссылками?

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

Ответы [ 5 ]

10 голосов
/ 27 сентября 2010

Из-за возможных ошибок округления может быть трудно подобрать правильный момент шаблона.

Примите во внимание следующее:

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; 

«Что должен сгенерировать компилятор? Компилятор должен знать о деталях целевой архитектуры с плавающей запятой, чтобы иметь возможность запускать создание экземпляра шаблона. Это достаточно просто, если компилятор работает на целевой архитектуре, он может просто выполнить расчети выясните ответ, но если вы выполняете кросс-компиляцию, компилятор должен иметь возможность синтезировать поведение с плавающей запятой для каждой предполагаемой целевой архитектуры. И, к счастью, Комитет по стандартам решил, что это было бы неразумно. "

Бесстыдно скопировано с здесь.

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

Согласно моему пониманию не типовой параметр не может быть типа класса, потому что может быть более одной реализации класса.Например,

template <typename T>   
class demo{...};

template <>    
class demo<int>{...};


template <typename T, demo d> //which demo?? demo<T> or demo<int>
class Example{...};

Локальные классы не могут использоваться в качестве параметров шаблона, поскольку они не имеют внешней связи .

5 голосов
/ 27 сентября 2010

Числа с плавающей запятой не имеют универсального представления (и некоторые значения даже не могут быть представлены без потери точности, поскольку основаны на приближении) и поэтому могут отличаться от платформы к платформе, что приводит к тому, что один и тот же код C ++ генерирует разные шаблоны на разных платформах.

( Можно сказать, что C ++ поддерживает кросс-компиляцию, не требуя, чтобы компилятор полностью эмулировал арифметику с плавающей запятой целевой машины. Разрешение float или double в качестве параметра шаблона сделает это недействительным. )

template<float F> 
float squared_float() 
{ 
return F * F;
}

Например, squared_float <1.0> может быть той же функцией, что и squared_float <1.00000001> в некоторых реализациях, тогда как в других это будут две разные функции.


reference to a float означает, что компилятор может оптимизировать его, поскольку он знает, что это значение, и что он никогда не должен изменяться.

Для pointer, это просто еще один тип данных, зависящий от архитектуры (32-бит / 64-бит), который не имеет ничего общего с float.

1 голос
/ 05 декабря 2011

Решением этой проблемы является использование рациональных чисел.Отправьте два целочисленных нетиповых параметра, а затем инициализируйте ваш float в конструкторе следующим образом

template<int dNum =1, int dDen = 3>
class myclass {
  double d;
  myclass: d(dNum/dDen) {}
};

вуаля, передавая float.

1 голос
/ 27 сентября 2010

Точное кодирование значений с плавающей запятой в большей степени зависит от особенностей отдельных процессоров.Например, при оценке предположительно константного выражения должен ли ЦП использовать более точные 80-битные регистры ЦП и только округлять до 64-битных в конце?Если один компилятор скажет да, а другой нет - шаблон получит два различных экземпляра.Но некоторые другие компиляторы могут иметь только 64-битные регистры, и, возможно, разные процессоры могут отличаться в зависимости от значения epsilon.Порядок, в котором какой-либо компилятор выбирает оценку выражения, или то, была ли библиотека скомпилирована с использованием библиотеки эмуляции с плавающей точкой и т. Д., Может привести к таким несоответствиям.Кроме того, числа с плавающей запятой имеют некоторые странные граничные случаи: положительный и отрицательный 0 и т. Д., Для которых должно быть определено поведение.

Эти проблемы могут потенциально укусить в средах, где объекты компилируются на разных машинах (с разнымиПроцессоры, версии компилятора и флаги и т. Д.), Но для надежной связи.Предприятия обычно делают это, и бинарно-распределенные библиотеки тоже сталкиваются с такими проблемами.Компиляторы C ++, как правило, пытаются использовать некоторый двоичный интерфейс приложений (ABI), который максимально согласован для версий и сред, но в настоящее время они не стандартизируют способ вычисления параметров с плавающей запятой, и не очевидно, как они могли бы, например, не ожидая, что все компиляторы будутиспользуйте ту же программную эмуляцию с плавающей точкой для получения значений.Это потребует усилий по координации, и существующие решения по эмуляции могут иметь проблемы с лицензированием.

Интересно, что Уолтер Брайт (из Digital Mars) думал, что это все дерьмо и позволяет использовать константы с плавающей точкой в ​​D ... думаю, он получал некоторыереальный опыт последствий, которые были бы полезны для сообщества C ++, но я недавно не слышал.

0 голосов
/ 01 апреля 2016

Возможное решение этой проблемы состоит в том, чтобы использовать тип с постоянным значением типа 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, упомянутого ранее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...