왜 float 값을 템플릿 매개 변수로 사용할 수 없습니까?


120

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'

Ron Penton의 "Data Structures for Game Programmers"를 읽고 있는데 저자는를 전달 float했지만 시도해도 컴파일되지 않는 것 같습니다.


1
저자는 실제로 사용 하는가 floatA와 비 형 템플릿 매개 변수 ? 어떤 챕터에 있나요?
K-ballo 2013 년

1
찾았습니다. "값을 템플릿 매개 변수로 사용"에 있습니다 ...
K-ballo

답변:


37

현재 C ++ 표준에서는 float(예 : 실수) 또는 문자열 리터럴을 템플릿 비 유형 매개 변수로 사용할 수 없습니다 . 물론 floatchar *유형을 일반 인수로 사용할 수 있습니다 .

아마도 저자가 현재 표준을 따르지 않는 컴파일러를 사용하고 있습니까?


8
에 대한 링크를 제공하거나 표준에서 관련 섹션으로 복사하십시오
thecoshman

2
@thecoshman 표준의 관련 섹션 + 자세한 정보는 내 (새로 게시 된) 답변에서 사용할 수 있습니다.
Filip Roséen-refp 07-07-17

1
C ++ 11에서는 문자열 리터럴을 템플릿 비 형식 매개 변수로 사용할 수 있습니다. 템플릿이 문자 팩을 사용하는 경우 template<char ...cs>컴파일 타임에 문자열 리터럴을 이러한 팩으로 변환 할 수 있습니다. 다음은 ideone에 대한 데모입니다 . (데모는 C ++ 14이지만, 다시 C ++ 11 포트를 쉽게 - std::integer_sequence유일한 어려움)
아론 McDaid

char &*다른 곳에서 리터럴을 정의하는 경우 템플릿 매개 변수로 사용할 수 있습니다 . 해결 방법으로 잘 작동합니다.
StenSoft

137

간단한 대답

표준은 부동 소수점을 유형이 아닌 template-arguments 로 허용하지 않습니다 . 이는 C ++ 11 표준의 다음 섹션에서 읽을 수 있습니다.

14.3.2 / 1 템플릿 비 유형 인수 [temp.arg.nontype]

유형이 아닌 템플릿 매개 변수에 대한 템플릿 인수는 다음 중 하나 여야합니다.

  • 정수 또는 열거 유형의 비 유형 템플릿 매개 변수의 경우 템플릿 매개 변수 유형의 변환 된 상수 표현식 (5.19)

  • 유형이 아닌 템플릿 매개 변수의 이름 또는

  • 정적 저장 기간과 외부 또는 내부 연결이있는 객체의 주소를 지정하는 상수 표현식 (5.19) 또는 함수 템플릿 및 함수 템플릿 ID를 포함하지만 비 정적 클래스 멤버를 제외하고 표현 된 (무시) ​​외부 또는 내부 연결이있는 함수 괄호) as & id-expression, 단, 이름이 함수 또는 배열을 참조하는 경우 &를 생략 할 수 있으며 해당 템플릿 매개 변수가 참조 인 경우 생략되어야합니다. 또는

  • 널 포인터 값 (4.10)으로 평가되는 상수 표현식 또는

  • 널 멤버 포인터 값 (4.11)으로 평가되는 상수 표현식 또는

  • 5.3.1에 설명 된대로 멤버에 대한 포인터.


근데 .. 근데 .. 왜!?

부동 소수점 계산을 정확한 방식으로 표현할 수 없기 때문일 수 있습니다. 만약 그것이 허용된다면, 이것과 같은 일을 할 때 잘못된 / 이상한 행동을 초래할 수 있습니다.

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

우리는 동일한 함수를 두 번 호출하려고했지만 두 계산의 부동 소수점 표현이 정확히 동일 하다는 보장이 없기 때문에 그렇지 않을 수도 있습니다 .


부동 소수점 값을 템플릿 인수로 어떻게 표현합니까?

으로 C++11당신은 꽤 고급 쓸 수있는 상수 표현식 ( constexpr 부동 값 컴파일 시간의 분자 / 분모를 계산 한 후 별도의 정수 인자로이 두 가지를 통과 할 것).

서로 가까운 부동 소수점 값이 동일한 분자 / 분모를 산출하도록 일종의 임계 값을 정의하는 것을 잊지 마십시오. 그렇지 않으면 부동 소수점 값을 유형이 아닌 값으로 허용하지 않는 이유로 이전에 언급 한 동일한 결과를 산출하기 때문에 다소 무의미 합니다. 템플릿 인수 .


56
C ++ 11 솔루션은 <ratio>§20.10에서 "Compile-time rational arithmetic"으로 설명됩니다. 귀하의 예에 맞습니다.
Potatoswatter

1
@Potatoswatter afaik STL에는 <ratio>?를 사용하여 부동 소수점을 분자 / 분모로 변환하는 방법이 없습니다 .
Filip Roséen-refp

3
이것은 실제로 설득력있는 설명을 제공하지 않습니다. 전체 지점 부동 소수점의는 정확하게 값을 나타냅니다 않는다는 것입니다. 가지고있는 숫자를 다른 것에 대한 근사치로 자유롭게 취급 할 수 있으며 그렇게하는 것이 종종 유용하지만 숫자 자체는 정확합니다.
tmyklebu 2015 년

4
@ FilipRoséen-refp : 모든 부동 소수점 숫자는 정확합니다. 부동 소수점 산술은 내가 아는 모든 대상에서 잘 정의되어 있습니다. 대부분의 부동 소수점 연산은 부동 소수점 결과를 생성합니다. 나는 컴파일러 구현 자들이 대상의 기괴한 부동 소수점 산술을 구현하도록 강요하고 싶지 않은위원회에 감사 할 수 있지만, "산술이 정수 산술과 다르다"는 것이 부동 소수점 템플릿 인수를 금지하는 좋은 이유라고 생각하지 않습니다. 하루가 끝나면 임의의 제한입니다.
tmyklebu

5
@iheanyi : 표준이 무엇을 말합니까 12345 * 12345? (그것은 수행 할 수 int는 서명 INT의 또는 표현 UB 여부 폭을 지정하지 않는 경우에도 템플릿 매개 변수를.)
tmyklebu

34

이것이 제한 인 이유 중 하나를 제공하기 위해서입니다 (적어도 현재 표준에서는).

템플릿 전문화를 일치시킬 때 컴파일러는 유형이 아닌 인수를 포함하여 템플릿 인수를 일치시킵니다.

본질적으로 부동 소수점 값은 정확하지 않으며 구현이 C ++ 표준에 의해 지정되지 않습니다. 결과적으로 두 개의 부동 소수점 비 유형 인수가 실제로 일치하는지 결정하기가 어렵습니다.

template <float f> void foo () ;

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

이러한 표현은 반드시 동일한 "비트 패턴"을 생성하는 것은 아니므로이를 다루기위한 특별한 표현 없이는 동일한 전문화를 사용한다고 보장 할 수 없습니다.


16
이것은 언어에서 수레를 완전히 금지하는 거의 논쟁입니다. 또는, 최소한 ==연산자를 금지하십시오. :-) 우리는 이미 런타임에이 부정확성을 받아들입니다. 컴파일 시간에도 마찬가지입니다.
Aaron McDaid

3
@AaronMcDaid와 동의하십시오. 이것은 많은 논쟁이 아닙니다. 따라서 정의에주의해야합니다. 그래서 뭐? 상수에서 얻은 항목에 대해 작동하는 한 이미 상당히 개선되었습니다.
einpoklum

1
C ++ 20은 이제 유형이 아닌 템플릿 매개 변수로 float (다른 객체 유형)을 허용합니다. 여전히 C ++ 20은 float 구현을 지정하지 않습니다. 이것은 einpoklum과 Aaron이 포인트를 가지고 있음을 보여줍니다.
Andreas H.

20

실제로 float 리터럴을 템플릿 매개 변수로 사용할 수 없습니다. 표준의 섹션 14.1 ( "비 유형 템플릿 매개 변수는 다음 (선택적으로 cv-qualified) 유형 중 하나를 가져야한다 ...") 참조 .

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;

11
할 수 있습니다. 하지만 같은 일을하지 않습니다. 참조를 컴파일 타임 상수로 사용할 수 없습니다.

12

자체 클래스의 매개 변수를 constexprs로 래핑합니다. 사실상 이것은 float 세트로 클래스를 매개 변수화하므로 트레이 트와 유사합니다.

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 및 double을 constexpr로 사용할 수 있습니다.


5

유형별로 고정 된 기본값을 사용하는 것이 괜찮다면 유형을 생성하여 상수로 정의하고 필요에 따라 특수화 할 수 있습니다.

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>;
 };

2

다른 답변은 부동 소수점 템플릿 매개 변수를 원하지 않는 이유를 제공하지만 실제 제 동자 IMO는 '=='를 사용하는 동등성과 비트 동등성이 동일하지 않다는 것입니다.

  1. -0.0 == 0.0하지만 0.0-0.0동일하지 않은 비트 단위

  2. NAN != NAN

두 종류의 평등은 형식 평등에 대한 좋은 후보가 아닙니다. 물론 2 번 항목은 ==형식 평등을 결정 하는 데 유효하지 않게 사용 합니다. 하나는 대신 비트 평등을 사용하지만, 할 수 x != y있다는 것을 의미하지는 않습니다 MyClass<x>그리고 MyClass<y>오히려 이상한 것 (2에 의해) 다른 유형이다.


1

당신은 항상 그것을 속일 수 있습니다 ...

#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-in-templates-wtf.html


3
A float! = 유리수. 둘은 매우 별개의 아이디어입니다. 하나는 가수와 지수를 통해 계산되고, 다른 하나는 합리적입니다 float. 합리적으로 표현할 수있는 모든 값이 .
Richard J. Ross III

2
@ RichardJ.RossIII A float는 확실히 합리적인 수이지만 float2 int의 비율로 표현할 수없는 s가 있습니다. 가수는 정수이고 2 ^ 지수는 정수
Caleth

1

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;
}

참조 아마 더 나은의 '@moonshadow 볼 수있다 대답
einpoklum

1
이것이 실제로 컴파일 타임에 제대로 감소합니까?
Ant6n

1

C ++ 20부터 가능합니다 .

이것은 또한 원래 질문에 대한 답을 제공합니다.

Why can't I use float value as a template parameter?

아무도 아직 표준에서 구현하지 않았기 때문입니다. 근본적인 이유는 없습니다.

C ++ 20에서 형식이 아닌 템플릿 매개 변수는 이제 float 및 클래스 객체가 될 수 있습니다.

클래스 객체에 대한 몇 가지 요구 사항 ( 리터럴 유형 이어야 함 )이 있으며 사용자 정의 연산자 == ( 세부 정보 ) 합니다.

우리는 심지어 사용할 수 있습니다 auto

template <auto Val>
struct Test {
};

struct A {};
static A aval;
Test<aval>  ta;
Test<A{}>  ta2;
Test<1.234>  tf;
Test<1U>  ti;

GCC 9 (및 10)는 클래스 비 유형 템플릿 매개 변수를 구현 하지만 아직 float에 대해서는 구현 하지 않습니다 .


0

고정 된 정밀도 만 나타내려면 이와 같은 기술을 사용하여 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)>
// ...

그것은해야한다 ...::Factor = _Factor_/100.0;, 그렇지 않으면 분열 정수가됩니다.
alfC 2014
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.