Neden float değerini şablon parametresi olarak kullanamıyorum?


120

Bir floatşablon parametresi olarak kullanmaya çalıştığımda , derleyici bu kod için ağlıyor, ancak intiyi çalışıyor.

floatŞablon parametresi olarak kullanamadığım için mi?

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

Hata:

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'ın "Oyun Programcıları için Veri Yapıları" nı okuyorum , yazar a'yı geçiyor float, ancak denediğimde derleme gibi görünmüyor.


1
Yazar gerçekten tür olmayanfloat bir şablon parametresi olarak kullanıyor mu? Bu hangi bölümde?
K-ballo

1
Buldum, "Değerleri Şablon Parametreleri Olarak Kullanmak" adresinde ...
K-ballo

Yanıtlar:


37

Geçerli C ++ standardı float(yani gerçek sayı) veya karakter dizisi değişmez değerlerinin şablon tipi olmayan parametreler olarak kullanılmasına izin vermez . Elbette floatve char *türlerini normal argümanlar olarak kullanabilirsiniz .

Belki de yazar mevcut standardı takip etmeyen bir derleyici kullanıyor?


8
Lütfen standarttan ilgili bölümün bir bağlantısını veya kopyasını sağlayın
thecoshman

2
@thecoshman standartın ilgili bölümü + daha fazla bilgi (yeni gönderilen) cevabımda mevcuttur.
Filip Roséen - refp

1
C ++ 11'de, şablon tipi olmayan bir parametre olarak bir karakter dizesi değişmezi kullanmak neredeyse mümkündür. Şablonunuz bir karakter paketi alıyorsa template<char ...cs>, dizge hazır bilgisi derleme zamanında böyle bir pakete dönüştürülebilir. İşte ideone ile ilgili bir demo . (Demo C ++ 14'tür, ancak onu C ++ 11'e geri taşımak kolaydır - std::integer_sequencetek zorluk budur)
Aaron McDaid

char &*Değişmezi başka bir yerde tanımlarsanız, şablon parametresi olarak kullanabileceğinizi unutmayın . Bir geçici çözüm olarak oldukça iyi çalışıyor.
StenSoft

137

BASİT CEVAP

Standart, kayan noktaların tür olmayan şablon argümanları olarak kullanılmasına izin vermez , bu C ++ 11 standardının aşağıdaki bölümünde okunabilir;

14.3.2 / 1 Şablon tip dışı argümanlar [temp.arg.nontype]

Tür olmayan, şablon olmayan şablon parametresi için bir şablon bağımsız değişkeni şunlardan biri olmalıdır:

  • integral veya numaralandırma tipinde tip olmayan bir şablon parametresi için şablon parametresinin türünün dönüştürülmüş bir sabit ifadesi (5.19);

  • tür olmayan bir şablon parametresinin adı; veya

  • statik depolama süresi ve harici veya dahili bağlantıya sahip bir nesnenin adresini veya fonksiyon şablonları ve fonksiyon şablon kimlikleri dahil, ancak statik olmayan sınıf üyelerini hariç tutan harici veya dahili bağlantıya sahip bir fonksiyonun adresini belirten sabit bir ifade (5.19) ifade edilir (yok sayılır) parantezler) & id-ifadesi olarak, ancak ad bir işleve veya diziye atıfta bulunuyorsa & atlanabilir ve karşılık gelen şablon-parametresi bir referanssa atlanabilir; veya

  • boş işaretçi değeri (4.10) olarak değerlendirilen sabit bir ifade; veya

  • boş üye işaretçi değeri (4.11) olarak değerlendirilen sabit bir ifade; veya

  • 5.3.1'de açıklandığı gibi ifade edilen üyeye bir işaretçi.


Ama .. ama .. NEDEN !?

Muhtemelen kayan nokta hesaplamalarının kesin bir şekilde temsil edilememesinden kaynaklanmaktadır. İzin verilseydi, böyle bir şey yaparken hatalı / tuhaf davranışlara neden olabilir / sonuçlanırdı;

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

Aynı işlevi iki kez çağırmak istiyorduk, ancak iki hesaplamanın kayan nokta gösteriminin tam olarak aynı olacağı garanti edilmediğinden, durum bu olmayabilir .


Kayan nokta değerlerini şablon bağımsız değişkenleri olarak nasıl temsil ederim?

İle , bir kayan değer derleme süresinin payını / paydasını hesaplayacak C++11oldukça gelişmiş sabit ifadeler ( constexpr ) yazabilir ve sonra bu ikisini ayrı tamsayı argümanları olarak geçirebilirsiniz.

Birbirine yakın kayan nokta değerlerinin aynı payı / paydayı vermesi için bir tür eşik tanımlamayı unutmayın , aksi takdirde, daha önce kayan nokta değerlerine tür olmayan olarak izin vermeme nedeni olarak belirtilen aynı sonucu vereceği için biraz anlamsızdır. şablon argümanları .


56
C ++ 11 çözümü, <ratio>§20.10 tarafından "Derleme zamanı rasyonel aritmetiği" olarak tanımlanmıştır. Sizin örneğinizi kesen.
Potatoswatter

1
@Potatoswatter afaik kullanarak pay / payda haline bir float dönüştürmek için STL herhangi bir yöntem yoktur <ratio>?
Filip Roséen - refp

3
Bu gerçekten ikna edici bir açıklama yapmıyor. Kayan noktanın tüm noktası , değerleri tam olarak temsil etmesidir. Elinizdeki sayıları başka bir şeye yaklaşık olarak ele almakta özgürsünüz ve bunu yapmak genellikle yararlıdır, ancak sayıların kendisi doğrudur.
tmyklebu

4
@ FilipRoséen-refp: Tüm kayan noktalı sayılar tamdır. Kayan nokta aritmetiği bildiğim her hedefte iyi tanımlanmıştır. Çoğu kayan nokta işlemi, kayan nokta sonuçları üretir. Derleyici uygulayıcılarını hedefin muhtemelen tuhaf kayan nokta aritmetiğini uygulamaya zorlamak istemeyen komiteyi takdir edebilirim, ancak "aritmetiğin tam sayı aritmetiğinden farklı olduğunu" kayan noktalı şablon argümanlarını yasaklamak için iyi bir neden olduğuna inanmıyorum. Günün sonunda keyfi bir kısıtlama.
tmyklebu

5
@iheanyi: Standart ne 12345 * 12345olduğunu söylüyor mu? (O does izin into imzalı int veya bu ifade UB olup olmadığı genişliğini belirtmez rağmen şablon parametrelerini.)
tmyklebu

34

Bunun bir sınırlama olmasının nedenlerinden birini sağlamak için (en azından mevcut standartta).

Şablon uzmanlıklarını eşleştirirken, derleyici, tür olmayan bağımsız değişkenler dahil olmak üzere şablon bağımsız değişkenleriyle eşleşir.

Doğası gereği, kayan nokta değerleri kesin değildir ve uygulamaları C ++ standardı tarafından belirtilmemiştir. Sonuç olarak, iki kayan noktalı tip olmayan bağımsız değişkenin ne zaman eşleştiğine karar vermek zordur:

template <float f> void foo () ;

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

Bu ifadeler mutlaka aynı "bit desenini" üretmez ve bu nedenle aynı uzmanlığı kullandıklarını garanti etmek mümkün olmaz - bunu kapsayacak özel bir ifade olmadan.


16
Bu neredeyse tamamen dilden yüzen bir yasaklama argümanıdır. Ya da en azından ==operatörü yasaklayın :-) Bu yanlışlığı çalışma zamanında zaten kabul ediyoruz, neden derleme zamanında da değil?
Aaron McDaid

3
@AaronMcDaid ile aynı fikirdeyim, bu pek bir argüman değil. Bu yüzden tanımda dikkatli olmalısınız. Ne olmuş yani? Sabitlerden aldığınız şeyler için çalıştığı sürece, zaten oldukça gelişmiştir.
einpoklum

1
C ++ 20 artık tip olmayan şablon parametreleri olarak float'a (başka bir nesne türü) izin vermektedir. Yine de C ++ 20, float uygulamasını belirtmez. Bu, einpoklum ve Aaron'un haklı olduğunu gösteriyor.
Andreas H.

20

Aslında, float değişmezlerini şablon parametreleri olarak kullanamazsınız. Bkz. Bölüm 14.1 ("Tip dışı bir şablon parametresi aşağıdaki (isteğe bağlı olarak cv nitelikli) tiplerden birine sahip olmalıdır ...") .

Bir şablon parametresi olarak şamandıraya bir başvuru kullanabilirsiniz:

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
Yapabilirsin. ama aynı şeyi yapmıyor. Referansı derleme zamanı sabiti olarak kullanamazsınız.

12

Parametre (ler) i kendi sınıflarına sabitler olarak sarın. Etkili olarak bu, sınıfı bir dizi kayan nokta ile parametrelendirdiği için bir özelliğe benzer.

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

ve sonra sınıf türünü parametre olarak alan bir şablon oluşturun

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

ve sonra onu böyle kullanın ...

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

Bu, derleyicinin aynı parametre paketiyle her şablon somutlaştırması için yalnızca tek bir kod örneğinin oluşturulmasını garanti etmesine olanak tanır. Bu, tüm sorunların üstesinden gelir ve templated sınıf içinde float ve double'ları constexpr olarak kullanabilirsiniz.


5

Her tür için sabit bir varsayılanınız varsa, onu sabit olarak tanımlamak ve gerektiği gibi özelleştirmek için bir tür oluşturabilirsiniz.

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'iniz varsa, varsayılan değeri tanımlarken constexpr kullanabilirsiniz. C ++ 14 ile MyTypeDefault, sözdizimsel olarak biraz daha temiz olan bir şablon değişkeni olabilir.

//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

Diğer yanıtlar, kayan noktalı şablon parametrelerini muhtemelen istemediğiniz için iyi nedenler verir, ancak gerçek anlaşmayı engelleyen IMO, '==' kullanan eşitliğin ve bitsel eşitliğin aynı olmadığıdır:

  1. -0.0 == 0.0Ama 0.0ve -0.0eşit bit bazında değildir

  2. NAN != NAN

Her iki tür eşitlik de tip eşitliği için iyi bir sonuç değildir: Elbette, 2. madde, ==tip eşitliğini belirlemek için geçersiz kılar . Bunun yerine bitsel eşitlik kullanılabilir, ancak o x != yzaman bunu ima etmez MyClass<x>ve MyClass<y>farklı türler (2'ye göre), ki bu oldukça garip olurdu.


1

Her zaman taklit edebilirsiniz ...

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

Referans: http://code-slim-jim.blogspot.jp/2013/06/c11-no-floats-in-templates-wtf.html


3
A float! = Rasyonel sayı. İkisi çok ayrı fikirlerdir. Biri mantis ve üs aracılığıyla hesaplanır, diğeri rasyoneldir - bir rasyonel tarafından temsil edilebilen her değer a ile gösterilemez float.
Richard J. Ross III

2
@ RichardJ.RossIII A floatkesinlikle bir rasyonel sayıdır, ancak floatiki ints'nin oranları olarak temsil edilemeyen ler vardır. Mantis bir
Tamsayıdır

1

Bir derleme zamanı sabiti olmak için double'a ihtiyacınız yoksa, onu bir işaretçi olarak geçirebilirsiniz:

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

Bir referans muhtemelen daha iyidir, @moonshadow'un cevabına bakın
einpoklum

1
Bu, derleme zamanında gerçekten düzgün bir şekilde azalır mı?
Ant6n

1

C ++ 20 ile başlayarak bu mümkündür .

Bu aynı zamanda orijinal sorunun cevabını verir:

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

Çünkü henüz kimse standartta uygulamadı. Temel bir sebep yok.

C ++ 20'de tipsiz şablon parametreleri artık yüzer ve hatta sınıf nesneleri olabilir.

Sınıf nesnelerinde bazı gereksinimler vardır (bunlar birebir tür olmalıdır ) ve kullanıcı tanımlı operatör == ( Ayrıntılar ) gibi patolojik durumları dışlamak için bazı diğer gereksinimleri karşılar .

Hatta kullanabiliriz 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'un (ve 10) sınıf tipi olmayan şablon parametrelerini uyguladığını, ancak henüz kayan değerler için olmadığını unutmayın .


0

Yalnızca sabit bir kesinliği temsil etmek istiyorsanız, float parametresini int'e dönüştürmek için bunun gibi bir teknik kullanabilirsiniz.

Örneğin, 1,75 büyüme faktörüne sahip bir dizi, 2 basamaklı kesinlik varsayılarak (100'e bölün) aşağıdaki gibi oluşturulabilir.

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;

Şablon argüman listesinde 1.75'in 175 olarak gösterilmesini beğenmezseniz, onu her zaman bir makroya sarabilirsiniz.

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

template <typename _Kind_, int _Factor_=FloatToIntPrecision(1.75,2)>
// ...

Olması gereken ...::Factor = _Factor_/100.0;aksi takdirde bölünme tamsayı olacaktır.
alfC
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.