C ++: template & lt; typename T, T t & gt; и создание экземпляра с помощью float [duplicate]

Как будто вы пытаетесь получить доступ к объекту, который является null. Рассмотрим ниже пример:

TypeA objA;

. В это время вы только что объявили этот объект, но не инициализировали или не инициализировали. И всякий раз, когда вы пытаетесь получить доступ к каким-либо свойствам или методам в нем, он будет генерировать NullPointerException, что имеет смысл.

См. Также этот пример:

String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
91
задан Filip Roséen - refp 2 March 2014 в 13:53
поделиться

9 ответов

Текущий стандарт C ++ не позволяет использовать float (то есть действительный номер) или литералы символьной строки в качестве параметров непикового шаблона . Конечно, вы можете использовать типы float и char * в качестве обычных аргументов.

Возможно, автор использует компилятор, который не соответствует текущему стандарту?

31
ответ дан Filip Roséen - refp 20 August 2018 в 09:30
поделиться
  • 1
    Предоставьте ссылку или копию соответствующего раздела из стандарта – thecoshman 17 July 2012 в 09:37
  • 2
    @thecoshman соответствующий раздел стандарта + дополнительная информация доступна в моем (недавно опубликованном) ответе. – Filip Roséen - refp 17 July 2012 в 11:10
  • 3
    В C ++ 11, возможно, можно использовать литерал символьной строки как шаблонный непиковый параметр. Если ваш шаблон принимает пакет символов template<char ...cs>, тогда строковый литерал может быть преобразован в такой пакет во время компиляции. Вот демо на ideone . (Демо - это C ++ 14, но его легко перенести обратно в C ++ 11 - std::integer_sequence - единственная проблема) – Aaron McDaid 20 July 2015 в 20:05
  • 4
    Обратите внимание, что вы можете использовать char &* в качестве параметра шаблона, если вы определяете литерал где-то в другом месте. Хорошо работает как обходной путь. – StenSoft 29 July 2015 в 10:11

Оберните параметр (ы) в своем классе как constexprs. Эффективно это похоже на черту, поскольку она параметризует класс с набором поплавков.

class MyParameters{
    public:
        static constexpr float Kd =1.0f;
        static constexpr float Ki =1.0f;
        static constexpr float Kp =1.0f;
};

, а затем создает шаблон, в котором используется тип класса в качестве параметра

  template <typename NUM, typename TUNING_PARAMS >
  class PidController {

      // define short hand constants for the PID tuning parameters
      static constexpr NUM Kp = TUNING_PARAMS::Kp;
      static constexpr NUM Ki = TUNING_PARAMS::Ki;
      static constexpr NUM Kd = TUNING_PARAMS::Kd;

      .... code to actually do something ...
};

а затем использовать его так ...

int main (){
    PidController<float, MyParameters> controller;
    ...
    ...
}

Это позволяет компилятору гарантировать, что для каждого экземпляра шаблона с одним и тем же пакетом параметров создается только один экземпляр кода. Это касается всех проблем, и вы можете использовать float и doubles как constexpr внутри шаблонного класса.

5
ответ дан Andrew Goedhart 20 August 2018 в 09:30
поделиться

Вы всегда можете подделать его ...

#include <iostream>

template <int NUM, int DEN>
struct Float
{
    static constexpr float value() { return (float)NUM / (float)DEN; }
    static constexpr float VALUE = value();
};

template <class GRAD, class CONST>
struct LinearFunc
{
    static float func(float x) { return GRAD::VALUE*x + CONST::VALUE; }
};


int main()
{
    // Y = 0.333 x + 0.2
    // x=2, y=0.866
    std::cout << " func(2) = "
              << LinearFunc<Float<1,3>, Float<1,5> > ::func(2) << std::endl;
}

Ссылка: http://code-slim-jim.blogspot.jp/2013/06/c11-no-floats- в-шаблоны-wtf.html

1
ответ дан Ashley Smart 20 August 2018 в 09:30
поделиться
  • 1
    A float! = Рациональное число. Эти две очень разные идеи. Один рассчитан с помощью мантиссы & amp; показатель, другой, ну, рациональный - не каждое значение, представляемое рациональным, представимо а float. – Richard J. Ross III 8 June 2013 в 18:52
  • 2
    @ RichardJ.RossIII A float очень определенно является рациональным числом, но существуют float s, которые не представляются как отношения двух int s. Мантисса является целым числом, а показатель 2 ^ - целым числом – Caleth 16 June 2017 в 12:29

Если вы хотите представлять только фиксированную точность, то вы можете использовать такую ​​технику, чтобы преобразовать параметр float в int.

Например, может быть создан массив с коэффициентом роста 1,75 следующим образом, предполагая 2 цифры точности (разделите на 100).

template <typename _Kind_, int _Factor_=175>
class Array
{
public:
    static const float Factor;
    _Kind_ * Data;
    int Size;

    // ...

    void Resize()
    {
         _Kind_ * data = new _Kind_[(Size*Factor)+1];

         // ...
    }
}

template<typename _Kind_, int _Factor_>
const float Array<_kind_,_Factor_>::Factor = _Factor_/100;

Если вам не нравится представление 1,75 как 175 в списке аргументов шаблона, вы всегда можете перенести его в некоторый макрос.

#define FloatToIntPrecision(f,p) (f*(10^p))

template <typename _Kind_, int _Factor_=FloatToIntPrecision(1.75,2)>
// ...
0
ответ дан jurujen 20 August 2018 в 09:30
поделиться
  • 1
    он должен быть ...::Factor = _Factor_/100.0;, иначе он будет целым делением. – alfC 6 November 2014 в 01:19

Если у вас нормально фиксированное значение по умолчанию для типа, вы можете создать тип, чтобы определить его как константу и специализировать его по мере необходимости.

template <typename T> struct MyTypeDefault { static const T value; };
template <typename T> const T MyTypeDefault<T>::value = T();
template <> struct MyTypeDefault<double> { static const double value; };
const double MyTypeDefault<double>::value = 1.0;

template <typename T>
class MyType {
  public:
    MyType() { value = MyTypeDefault<T>::value; }
  private:
    T value;
 };

Если у вас есть C ++ 11, вы можете используйте constexpr при определении значения по умолчанию. С C ++ 14 MyTypeDefault может быть переменной шаблона, которая синтаксически немного чиста.

//C++14
template <typename T> constexpr T MyTypeDefault = T();
template <> constexpr double MyTypeDefault<double> = 1.0;

template <typename T>
class MyType {
  private:
    T value = MyTypeDefault<T>;
 };
4
ответ дан Matthew Fioravante 20 August 2018 в 09:30
поделиться

Действительно, вы не можете использовать float-литералы в качестве параметров шаблона. См. Раздел 14.1 («Параметр шаблона, не относящийся к типу, должен иметь один из следующих (необязательно cv-квалифицированных) типов ...») стандарта.

Вы можете использовать ссылку на float в качестве параметра шаблона:

template <class T, T const &defaultValue>
class GenericClass

.
.

float const c_four_point_six = 4.6; // at global scope

.
.

GenericClass < float, c_four_point_six> gcFlaot;
17
ответ дан moonshadow 20 August 2018 в 09:30
поделиться
  • 1
    Ты можешь. но это не делает то же самое. Вы не можете использовать ссылку как константу времени компиляции. – user 2 February 2010 в 11:17

Просто для того, чтобы указать одну из причин, почему это ограничение (по крайней мере в текущем стандарте).

При сопоставлении специализированных шаблонов компилятор сопоставляет аргументы шаблона, в том числе аргументы не-типа.

По самой своей природе значения с плавающей запятой не являются точными, и их реализация не указана стандартом C ++. В результате трудно решить, когда два аргумента non-типа с плавающей запятой действительно соответствуют:

template <float f> void foo () ;

void bar () {
    foo< (1.0/3.0) > ();
    foo< (7.0/21.0) > ();
}

Эти выражения не обязательно приводят к одному и тому же «битовому шаблону», и поэтому было бы невозможно гарантировать что они использовали ту же специализацию - без специальной формулировки, чтобы покрыть это.

31
ответ дан Richard Corden 20 August 2018 в 09:30
поделиться
  • 1
    Это почти аргумент, чтобы полностью запретить плавать с языка. Или, как минимум, запретите оператор == :-) Мы уже принимаем эту неточность во время выполнения, почему бы не во время компиляции тоже? – Aaron McDaid 4 August 2015 в 10:45
  • 2
    Согласитесь с @AaronMcDaid, это не большой аргумент. Поэтому вам нужно быть осторожным в определении. И что? Пока это работает на вещи, которые вы получаете от констант, это уже довольно улучшилось. – einpoklum 27 March 2016 в 00:20

Если вам не нужно, чтобы double являлся константой времени компиляции, вы можете передать ее как указатель:

#include <iostream>

extern const double kMyDouble = 0.1;;

template <const double* MyDouble>
void writeDouble() {
   std::cout << *MyDouble << std::endl; 
}

int main()
{
    writeDouble<&kMyDouble>();
   return 0;
}
1
ответ дан user3233025 20 August 2018 в 09:30
поделиться

ПРОСТОЙ ОТВЕТ

Стандарт не допускает плавающие точки как шаблонные аргументы не-типа , о которых можно прочитать в следующем разделе C ++ 11,

14.3.2 / 1 & nbsp; & nbsp; & nbsp; & nbsp; Шаблон не-типа аргументов & nbsp; & nbsp; & nbsp; & nbsp; [temp.arg.nontype]

Аргумент шаблона для непигового шаблона-шаблона без шаблона должен быть одним из:

  • для не-типа шаблон-параметр интегрального или перечисляемого типа, преобразованное константное выражение (5.19) типа шаблона-параметра;
  • имя непигового шаблона-параметра; или
  • константное выражение (5.19), которое обозначает адрес объекта со статической продолжительностью хранения и внешней или внутренней связью или функцию с внешней или внутренней связью, включая шаблоны функций и идентификаторы шаблонов функций, -статические члены класса, выраженные (игнорируя круглые скобки) как & amp; id-expression, за исключением того, что & amp; может быть опущено, если имя относится к функции или массиву и должно быть опущено, если соответствующий шаблон-параметр является ссылкой; или
  • константное выражение, которое вычисляет значение нулевого указателя (4.10); или
  • константное выражение, которое вычисляет значение указателя пустого элемента (4.11); или
  • указатель на элемент, выраженный как описано в 5.3.1.

Но .. но .. ПОЧЕМУ??

Вероятно, это связано с тем, что вычисления с плавающей запятой не могут быть представлены точно. Если это было разрешено, это могло бы / приводило бы к ошибочному / странному поведению при выполнении чего-то такого:

func<1/3.f> (); 
func<2/6.f> ();

Мы хотели дважды вызвать ту же функцию, но это может быть не так, поскольку представление с плавающей запятой


Как я представляю значения с плавающей запятой в качестве аргументов шаблона?

С помощью C++11 вы могли бы написать некоторые довольно продвинутые константные выражения ( constexpr ), которые вычисляли числитель / знаменатель времени компиляции с плавающим значением, а затем передавали эти два в виде отдельных целых аргументов.

Не забудьте определить какой-то порог, так что значения с плавающей запятой, близкие друг к другу, дают один и тот же числитель / знаменатель , в противном случае это бесполезно, так как тогда он даст тот же результат, который ранее упоминался как причина не разрешить значения плавающей запятой в качестве аргументов шаблона непигового типа .

110
ответ дан vladr 20 August 2018 в 09:30
поделиться
  • 1
    Решением C ++ 11 является <ratio>, описанное в §20.10 как «рациональная арифметика времени компиляции». Который разрезается прямо к вашему примеру. – Potatoswatter 17 July 2012 в 11:45
  • 2
    @Potatoswatter afaik нет никакого метода в STL для преобразования float в числитель / знаменатель с помощью <ratio>? – Filip Roséen - refp 17 July 2012 в 12:16
  • 3
    Это может иметь некоторое значение: stackoverflow.com/questions/95727/… – Gearoid Murphy 25 November 2012 в 19:54
  • 4
    @tmyklebu Я не уверен, как это относится к стандарту. Арифметика с плавающей запятой четко определена стандартом? Если нет, то, что каждая цель, о которой вы знаете, имеет четко определенную реализацию, несущественна. – iheanyi 19 January 2016 в 21:16
  • 5
    @iheanyi: Говорит ли стандарт, что 12345 * 12345? (It позволяет разрешать параметры шаблона int, даже если он не определяет ширину подписанного int или это выражение является UB.) – tmyklebu 26 January 2016 в 03:36
Другие вопросы по тегам:

Похожие вопросы: