मैं टेम्पलेट पैरामीटर के रूप में फ्लोट मान का उपयोग क्यों नहीं कर सकता हूं?


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'

मैं रॉन पेंटन द्वारा "डेटा स्ट्रक्चर्स फॉर गेम प्रोग्रामर्स" पढ़ रहा हूं , लेखक एक गुजरता है float, लेकिन जब मैं कोशिश करता हूं तो यह संकलन नहीं लगता है।


1
क्या लेखक वास्तव floatमें एक गैर-टाइप टेम्पलेट पैरामीटर के रूप में उपयोग करता है ? वह किस अध्याय में है?
के-बैलो '

1
यह मिला, यह "टेम्पलेट पैरामीटर्स के रूप में मूल्यों का उपयोग करना" पर है ...
के-बलू

जवाबों:


37

वर्तमान सी ++ मानक अनुमति नहीं देता है float(अर्थात वास्तविक संख्या) या चरित्र स्ट्रिंग शाब्दिकों को टेम्पलेट गैर-प्रकार के मापदंडों के रूप में उपयोग करने की अनुमति देता है । आप सामान्य तर्क के रूप में निश्चित रूप से floatऔर char *प्रकारों का उपयोग कर सकते हैं ।

शायद लेखक एक संकलक का उपयोग कर रहा है जो वर्तमान मानक का पालन नहीं करता है?


8
कृपया मानक से प्रासंगिक अनुभाग की एक लिंक या कॉपी प्रदान करें
thecoshman

2
@ मानदंड प्रासंगिक मानक + अधिक जानकारी मेरे (नए पोस्ट किए गए) उत्तर में उपलब्ध है।
फिलिप रोसेन -

1
C ++ 11 में, यह एक टेम्पलेट गैर-प्रकार पैरामीटर के रूप में एक चरित्र स्ट्रिंग शाब्दिक का उपयोग करना संभव है। यदि आपका टेम्पलेट एक चरित्र पैक लेता है template<char ...cs>, तो स्ट्रिंग शाब्दिक को संकलन के समय ऐसे पैक में परिवर्तित किया जा सकता है। यहाँ ideone पर एक डेमो है । (डेमो C ++ 14 है, लेकिन इसे C ++ 11 में वापस पोर्ट करना आसान है - std::integer_sequenceकेवल कठिनाई है)
हारून मैकडैड

ध्यान दें कि आप char &*टेम्पलेट पैरामीटर के रूप में उपयोग कर सकते हैं यदि आप कहीं और शाब्दिक परिभाषित करते हैं। वर्कअराउंड के रूप में बहुत अच्छी तरह से काम करता है।
StenSoft

137

सरल जवाब

मानक फ़्लोटिंग पॉइंट को गैर-प्रकार के टेम्पलेट-तर्क के रूप में अनुमति नहीं देता है , जिसे C ++ 11 मानक के निम्नलिखित अनुभाग के बारे में पढ़ा जा सकता है;

14.3.2 / 1 टेम्पलेट गैर-प्रकार के तर्क [temp.arg.nontype]

गैर-प्रकार, गैर-टेम्पलेट टेम्पलेट-पैरामीटर के लिए एक टेम्पलेट-तर्क निम्न में से एक होगा:

  • अभिन्न या गणना के गैर-प्रकार के टेम्पलेट-पैरामीटर के लिए, टेम्पलेट-पैरामीटर के प्रकार का एक परिवर्तित स्थिर अभिव्यक्ति (5.19);

  • एक गैर-प्रकार टेम्पलेट-पैरामीटर का नाम; या

  • एक स्थिर अभिव्यक्ति (5.19) जो स्थिर भंडारण अवधि और बाहरी या आंतरिक लिंकेज या फ़ंक्शन या बाहरी या आंतरिक लिंकेज के साथ एक फ़ंक्शन के पते को डिज़ाइन करती है, जिसमें फ़ंक्शन टेम्पलेट और फ़ंक्शन टेम्पलेट-आईडी शामिल हैं, लेकिन गैर-स्थिर वर्ग के सदस्यों को छोड़कर, व्यक्त (अनदेखा) कोष्ठक) के रूप में & आईडी-एक्सप्रेशन, सिवाय इसके कि & छोड़ा जा सकता है यदि नाम किसी फ़ंक्शन या सरणी को संदर्भित करता है और संबंधित टेम्पलेट-पैरामीटर एक संदर्भ है तो इसे छोड़ दिया जाएगा; या

  • एक स्थिर अभिव्यक्ति जो एक अशक्त सूचक मान (4.10) का मूल्यांकन करती है; या

  • एक स्थिर अभिव्यक्ति जो एक अशक्त सदस्य सूचक मान (4.11) का मूल्यांकन करती है; या

  • 5.3.1 में वर्णित सदस्य के लिए एक सूचक व्यक्त किया गया।


लेकिन .. लेकिन .. क्यों !?

यह शायद इस तथ्य के कारण है कि फ्लोटिंग पॉइंट गणनाओं का सटीक तरीके से प्रतिनिधित्व नहीं किया जा सकता है। यदि इसे अनुमति दी गई थी तो इसके परिणामस्वरूप कुछ करने पर गलत / अजीब व्यवहार हो सकता है;

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

हम एक ही फ़ंक्शन को दो बार कॉल करना चाहते थे, लेकिन यह मामला नहीं हो सकता है क्योंकि दो गणनाओं के फ़्लोटिंग पॉइंट प्रतिनिधित्व बिल्कुल समान होने की गारंटी नहीं है ।


टेम्प्लेट तर्कों के रूप में मैं फ़्लोटिंग पॉइंट मानों का प्रतिनिधित्व कैसे करूंगा?

साथ C++11आप कुछ बहुत उन्नत लिख सकता है निरंतर-भाव ( constexpr ) है कि एक अस्थायी मूल्य संकलन समय का अंश / भाजक गणना करेंगे और उसके बाद अलग पूर्णांक तर्क के रूप में इन दो गुजरती हैं।

किसी प्रकार की सीमा को परिभाषित करने के लिए याद रखें ताकि फ्लोटिंग पॉइंट वैल्यू एक-दूसरे के करीब एक ही अंश / भाजक को पैदावार दे , अन्यथा यह थोड़े व्यर्थ है क्योंकि यह उसी परिणाम को पहले ही बताएगा जो फ्लोटिंग पॉइंट मानों को गैर-प्रकार के रूप में अनुमति नहीं देता है। टेम्पलेट तर्क


56
C ++ 11 समाधान <ratio>, described20.10 द्वारा "संकलन-समय तर्कसंगत अंकगणित" के रूप में वर्णित है। जो आपके उदाहरण के लिए सही कटौती करता है।
पोटाटोज़वाटर

1
@Potatoswatter afaik एसटीएल में कोई भी तरीका नहीं है जो फ्लोट को अंश / हर में उपयोग करके परिवर्तित कर सकता है <ratio>?
फिलिप रोसेन -

3
यह वास्तव में एक ठोस व्याख्या नहीं देता है। फ्लोटिंग-पॉइंट की पूरी बात यह है कि यह बिल्कुल मूल्यों का प्रतिनिधित्व करता है। आपके पास उन संख्याओं का इलाज करने के लिए स्वतंत्र हैं, जिनके बारे में आपको कुछ और अनुमान है, और ऐसा करने के लिए अक्सर उपयोगी होता है, लेकिन संख्याएँ स्वयं सटीक होती हैं।
tmyklebu

4
@ FilipRoséen-refp: सभी फ्लोटिंग-पॉइंट नंबर सटीक हैं। फ्लोटिंग-पॉइंट अंकगणित मेरे द्वारा ज्ञात प्रत्येक लक्ष्य पर अच्छी तरह से परिभाषित है। अधिकांश फ्लोटिंग-पॉइंट ऑपरेशन फ्लोटिंग-पॉइंट परिणाम उत्पन्न करते हैं। मैं लक्ष्य के संभावित-विचित्र फ़्लोटिंग अंक अंकगणित को लागू करने के लिए कंपाइलर कार्यान्वयनकर्ताओं को बाध्य नहीं करने वाली समिति की सराहना कर सकता हूं, लेकिन मुझे विश्वास नहीं है कि "अंकगणित अंकगणित से अलग है" फ्लोटिंग-पॉइंट टेम्पलेट तर्क के लिए एक अच्छा कारण है। यह दिन के अंत में एक मनमाना प्रतिबंध है।
tmyklebu

5
@ जिहनी: क्या मानक कहता 12345 * 12345है? (यह करता है की अनुमति देते हैं int, भले ही यह एक हस्ताक्षरित पूर्णांक के या कि अभिव्यक्ति यूबी है चौड़ाई निर्दिष्ट नहीं करता टेम्प्लेट पैरामीटर।)
tmyklebu

34

सिर्फ एक कारण प्रदान करने के लिए कि यह एक सीमा क्यों है (वर्तमान मानक में कम से कम)।

टेम्प्लेट विशेषज्ञताओं का मिलान करते समय, कंपाइलर गैर-प्रकार के तर्कों सहित टेम्पलेट तर्कों से मेल खाता है।

उनके स्वभाव से, फ़्लोटिंग पॉइंट मान सटीक नहीं हैं और उनका कार्यान्वयन C ++ मानक द्वारा निर्दिष्ट नहीं है। परिणामस्वरूप, यह तय करना मुश्किल है कि दो फ्लोटिंग पॉइंट नॉन टाइप तर्क वास्तव में मेल खाते हैं:

template <float f> void foo () ;

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

ये अभिव्यक्तियाँ आवश्यक रूप से समान "बिट पैटर्न" का उत्पादन नहीं करती हैं और इसलिए यह गारंटी देना संभव नहीं होगा कि वे एक ही विशेषज्ञता का उपयोग करते थे - इसे कवर करने के लिए विशेष शब्द के बिना।


16
यह लगभग पूरी तरह से भाषा से तैरने पर प्रतिबंध लगाने का तर्क है। या, कम से कम, ==ऑपरेटर पर प्रतिबंध लगाओ :-) हम पहले से ही इस अशुद्धि को रनटाइम पर स्वीकार करते हैं, संकलन समय पर भी क्यों नहीं?
एरॉन मैकडैड

3
@AaronMcDaid से सहमत हों, यह बहुत तर्क नहीं है। इसलिए आपको परिभाषा में सावधानी बरतने की जरूरत है। तो क्या? जब तक यह आपको स्थिरांक से प्राप्त सामान के लिए काम करता है, यह पहले से ही काफी सुधार है।
ईनपोकलम

1
C ++ 20 अब गैर-टाइप टेम्पलेट मापदंडों के रूप में फ्लोट (एक अन्य वस्तु प्रकार) के लिए अनुमति देता है। फिर भी C ++ 20 फ्लोट कार्यान्वयन को निर्दिष्ट नहीं करता है। इससे पता चलता है कि ईनपोकलम और आरोन के पास एक बिंदु है।
एंड्रियास एच।

20

वास्तव में, आप फ्लोट शाब्दिकों को टेम्पलेट मापदंडों के रूप में उपयोग नहीं कर सकते हैं। खंड 14.1 देखें ("एक गैर-प्रकार का टेम्प्लेट-पैरामीटर मानक का निम्नलिखित (वैकल्पिक रूप से सीवी-योग्य) प्रकार ..." होगा

आप टेम्पलेट पैरामीटर के रूप में फ्लोट के संदर्भ का उपयोग कर सकते हैं:

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

अपने स्वयं के वर्ग में पैरामीटर (ओं) को रैपर्स के रूप में लपेटें। प्रभावी रूप से यह एक विशेषता के समान है क्योंकि यह फ्लोट के सेट के साथ वर्ग को मापता है।

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

यह कंपाइलर को यह गारंटी देता है कि कोड का केवल एक ही उदाहरण प्रत्येक पैरामीटर तात्कालिकता के लिए एक ही पैरामीटर पैक के साथ बनाया गया है। कि सभी मुद्दों के आसपास हो जाता है और आप अस्थायी वर्ग के रूप में अस्थायी वर्ग और युगल का उपयोग करने में सक्षम हैं।


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

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


3
float= तर्कसंगत संख्या। दोनों बहुत अलग विचार हैं। एक की गणना एक मंटिसा और एक प्रतिपादक के माध्यम से की जाती है, दूसरा, ठीक है, एक तर्कसंगत है - एक तर्कसंगत द्वारा प्रतिपादित प्रत्येक मूल्य एक द्वारा प्रतिनिधित्व करने योग्य नहीं है float
रिचर्ड जे। रॉस III

2
@ RichardJ.RossIII ए floatनिश्चित रूप से एक तर्कसंगत संख्या है, लेकिन ऐसे floatएस हैं जो दो intएस के अनुपात के रूप में प्रतिनिधित्व करने योग्य नहीं हैं ।
मंटिसा

1

यदि आपको कंपाइल-टाइम स्थिर होने के लिए डबल की आवश्यकता नहीं है, तो आप इसे पॉइंटर के रूप में पास कर सकते हैं:

#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 गैर-प्रकार के टेम्पलेट पैरामीटर अब फ़्लोट और यहां तक ​​कि क्लास ऑब्जेक्ट भी हो सकते हैं।

क्लास ऑब्जेक्ट्स पर कुछ आवश्यकताएं हैं (उन्हें शाब्दिक प्रकार होना चाहिए ) और पैथोलॉजिकल मामलों को बाहर करने के लिए कुछ अन्य आवश्यकताओं को पूरा करना जैसे कि उपयोगकर्ता परिभाषित ऑपरेटर == ( विवरण )।

हम भी उपयोग कर सकते हैं 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) वर्ग के गैर-प्रकार के टेम्पलेट मापदंडों को लागू करता है, लेकिन अभी तक तैरने के लिए नहीं


0

यदि आप केवल एक निश्चित सटीकता का प्रतिनिधित्व करना चाहते हैं, तो आप फ्लोट पैरामीटर को एक इंट में बदलने के लिए इस तरह की तकनीक का उपयोग कर सकते हैं।

उदाहरण के लिए 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;

यदि आप टेम्पलेट तर्क सूची में 175 के रूप में 1.75 के प्रतिनिधित्व को पसंद नहीं करते हैं तो आप इसे हमेशा कुछ मैक्रो में लपेट सकते हैं।

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

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

यह होना चाहिए ...::Factor = _Factor_/100.0;अन्यथा यह पूर्णांक विभाजन होगा।
alfC
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.