2010-05-03 6 views
14

मैं एक टेम्पलेट वेक्टर प्रकार के साथ एक सरल गणित पुस्तकालय लिख रहा हूँ:साझा कार्यक्षमता के साथ कक्षा टेम्पलेट विशेषज्ञताओं

template<typename T, size_t N> 
class Vector { 
    public: 
     Vector<T, N> &operator+=(Vector<T, N> const &other); 
     // ... more operators, functions ... 
}; 

अब मैं विशेष रूप से इनमें से कुछ के लिए कुछ अतिरिक्त कार्यक्षमता चाहते हैं। मान लें कि मैं विशेष निर्देशांक तक पहुंचने के लिए Vector<T, 2> पर x() और y() फ़ंक्शंस चाहता हूं। मैं इसके लिए आंशिक विशेषज्ञता बना सकता हूं:

template<typename T> 
class Vector<T, 3> { 
    public: 
     Vector<T, 3> &operator+=(Vector<T, 3> const &other); 
     // ... and again all the operators and functions ... 
     T x() const; 
     T y() const; 
}; 

लेकिन अब मैं जेनेरिक टेम्पलेट में पहले से मौजूद सब कुछ दोहरा रहा हूं।

मैं विरासत का भी उपयोग कर सकता हूं। VectorBase के लिए सामान्य टेम्प्लेट का नाम बदल रहा है, मैं ऐसा कर सकता है:

template<typename T, size_t N> 
class Vector : public VectorBase<T, N> { 
}; 

template<typename T> 
class Vector<T, 3> : public VectorBase<T, 3> { 
    public: 
     T x() const; 
     T y() const; 
}; 

हालांकि, अब समस्या यह है कि सभी ऑपरेटरों VectorBase पर निर्धारित किए जाते हैं, तो वे VectorBase उदाहरणों लौट आते हैं। ये Vector चर करने के लिए आवंटित नहीं किया जा सकता:

Vector<float, 3> v; 
Vector<float, 3> w; 
w = 5 * v; // error: no conversion from VectorBase<float, 3> to Vector<float, 3> 

मैं Vector एक अंतर्निहित रूपांतरण निर्माता दे सकता है यह संभव बनाने के लिए:

template<typename T, size_t N> 
class Vector : public VectorBase<T, N> { 
    public: 
     Vector(VectorBase<T, N> const &other); 
}; 

हालांकि, अब मैं Vector से VectorBase और फिर से वापस करने के लिए परिवर्तित कर रहा हूँ। यद्यपि प्रकार स्मृति में समान हैं, और संकलक इसे सब कुछ अनुकूलित कर सकते हैं, यह कट्टरपंथी लगता है और मुझे वास्तव में एक संकलन-समय की समस्या के लिए संभावित रन-टाइम ओवरहेड नहीं होना पसंद है।

क्या इसे हल करने का कोई और तरीका है?

+0

क्यों न केवल 'x() 'और' y() 'मुक्त फ़ंक्शन' वेक्टर 'के उचित विशेषज्ञता लेते हैं? जैसे 'टेम्पलेट टी एक्स (कॉन्स वेक्टर & v);' –

+0

संभव है, लेकिन 'vx()' 'x (v) 'से अधिक समझ में आता है। इसके अलावा, मैं कुछ विशेष रचनाकारों को जोड़ना चाहता हूं, उदाहरण के लिए' वेक्टर (टी, टी) ', और कन्स्ट्रक्टर मुक्त फ़ंक्शंस नहीं हो सकते हैं। – Thomas

+0

नहीं, लेकिन आपके पास ऐसे कार्य हो सकते हैं जो' std :: make_pair' की शैली में ऑब्जेक्ट्स लौटाते हैं। –

उत्तर

8

मुझे लगता है कि आप इस समस्या को हल करने के लिए CRTP का उपयोग कर सकते हैं। यह मुहावरे boost::operator में प्रयोग किया जाता है।

template<typename ChildT, typename T, int N> 
class VectorBase 
{  
public: 
    /* use static_cast if necessary as we know that 'ChildT' is a 'VectorBase' */ 
    friend ChildT operator*(double lhs, ChildT const &rhs) { /* */ } 
    friend ChildT operator*(ChildT const &lhs, double rhs) { /* */ } 
}; 

template<typename T, size_t N> 
class Vector : public VectorBase<Vector<T,N>, T, N> 
{ 
}; 

template<typename T> 
class Vector<T, 3> : public VectorBase<Vector<T, 3>, T, 3> 
{ 
public: 
    T x() const {} 
    T y() const {} 
}; 

void test() 
{ 
    Vector<float, 3> v; 
    Vector<float, 3> w; 
    w = 5 * v; 
    w = v * 5; 
    v.x(); 

    Vector<float, 5> y; 
    Vector<float, 5> z; 
    y = 5 * z; 
    y = z * 5; 
    //z.x(); // Error !! 
} 
+0

मैंने थोड़ी देर के लिए कुछ और काम करने के बाद इस समस्या का पुनरीक्षण किया, और यह वास्तव में सबसे अच्छा समाधान है। बहुत - बहुत धन्यवाद! – Thomas

4

कुछ ऐसा समय है जब मैं कुछ समय पहले सी ++ 0x सुविधाओं के साथ खेल रहा था। इसमें उपयोग की जाने वाली एकमात्र सी ++ 0x सुविधा static_assert है, इसलिए आप इसे बदलने के लिए बूस्ट का उपयोग कर सकते हैं।

असल में, हम एक स्थिर आकार चेक फ़ंक्शन का उपयोग कर सकते हैं जो केवल यह सुनिश्चित करने के लिए जांच करता है कि दिया गया इंडेक्स वेक्टर के आकार से कम है। हम एक स्थिर ज़ोर का उपयोग एक संकलक त्रुटि उत्पन्न करने के लिए करता है, तो सूचकांक सीमा से बाहर है:

template <std::size_t Index> 
void size_check_lt() const 
{ 
    static_assert(Index < N, "the index is not within the range of the vector"); 
} 

फिर हम एक get() विधि है कि (क दिए गए इंडेक्स पर तत्व के लिए संदर्भ स्पष्ट रूप से एक स्थिरांक अधिभार होगा रिटर्न प्रदान कर सकते हैं उपयोगी भी):

template <std::size_t Index> 
T& get() 
{ 
    size_check_lt<Index>(); return data_[Index]; 
} 

फिर हम तो जैसे सरल accessors लिख सकते हैं: वेक्टर केवल दो तत्व है

T& x() { return get<0>(); } 
T& y() { return get<1>(); } 
T& z() { return get<2>(); } 

हैं, तो आप x और y लेकिन जेड नहीं उपयोग कर सकते हैं। यदि वेक्टर में तीन या अधिक तत्व हैं तो आप तीनों का उपयोग कर सकते हैं।

मैं रचनाकारों के लिए एक ही काम कर रहा हूं - मैंने आयाम दो, तीन, और चार के वैक्टरों के लिए रचनाकार बनाए और size_check_eq जोड़ा जो उन्हें केवल आयाम दो, तीन, और चार के वैक्टरों के लिए तत्काल स्थापित करने की अनुमति देता है, क्रमशः। अगर कोई दिलचस्पी लेता है, तो मैं आज रात घर आने पर पूरा कोड आजमा सकता हूं और पोस्ट कर सकता हूं।

मैंने इस परियोजना को आधे रास्ते से गिरा दिया, इसलिए ऐसा करने में कुछ बड़ी समस्या हो सकती है कि मैंने भाग नहीं लिया ... कम से कम यह विचार करने का विकल्प है।

0

सबसे आसान तरीका? बाहरी फ़ंक्शन का उपयोग करके बाहरी कार्यों का उपयोग कर टेम्पलेट प्रोग्रामिंग में

template <class T> 
T& x(Vector<T,2>& vector) { return vector.at<0>(); } 

template <class T> 
T const& x(Vector<T,2> const& vector) { return vector.at<0>(); } 

सबसे आसान तरीका है बस विशेषज्ञता मुद्दा तुम सिर्फ का सामना करना पड़ा है, क्योंकि कार्यक्षमता जोड़ने के लिए है।

दूसरी ओर, यदि आप अभी भी प्रदान कर सकता है x, y और किसी भी N या शायद के लिए zenable_if/disable_if सुविधाओं का उपयोग दायरे को सीमित करने की।

0

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

template <typename T, std::size_t N> 
class fixed_array { 
public: 
    virtual ~fixed_array() {} 
    template <std::size_t K> 
    fixed_array& operator+=(fixed_array<T,K> const& other) { 
     for (std::size_t i=0; i<N; ++i) 
      this->contents[i] += other[i]; 
     return *this; 
    } 
    template <std::size_t K> 
    fixed_array& operator=(fixed_array<T,K> const& other) { 
     assign_from(other); 
     return *this; 
    } 
    T& operator[](std::size_t idx) { 
     if (idx >= N) 
      throw std::runtime_error("invalid index in fixed_array[]"); 
     return contents[idx]; 
    } 
protected: 
    template <std::size_t K> 
    void assign_from(fixed_array<T,K> const& other) { 
     for (std::size_t i=0; i<N; ++i) 
      this->contents[i] = other[i]; 
    } 
private: 
    T contents[N]; 
}; 

template <typename T> 
class fixed_2d_array: public fixed_array<T,2> { 
public: 
    T x_coord() const { return (*this)[0]; } 
    T y_coord() const { return (*this)[1]; } 
    template <std::size_t K> 
    fixed_2d_array& operator=(fixed_array<T,K> const& other) { 
     assign_from(other); 
     return *this; 
    } 
}; 

int 
main() { 
    fixed_array<int,5> ary1; 
    fixed_2d_array<int> ary2; 
    ary2 = ary1; 
    ary1 = ary2; 
    ary2 += ary1; 
    ary1 += ary2; 
    return 0; 
} 

 संबंधित मुद्दे

  • कोई संबंधित समस्या नहीं^_^