2010-03-11 28 views
7

C++ 'da iki ilkel tür verilen doğru tanıtım türünü belirlemek için bir dizi şablonla uğraştım. Buradaki fikir şudur: Özel bir sayısal şablon tanımlarsanız, bunları şablonlara iletilen sınıfa göre operatör + işlevinin dönüş türünü belirlemek için kullanabilirsiniz. Örneğin: Bu promosyon şablonları kullanıyorsanızC++ Aritmetik Tanıtım Üstbilgisi Kullanımları

// Custom numeric class 
template <class T> 
struct Complex { 
    Complex(T real, T imag) : r(real), i(imag) {} 
    T r, i; 
// Other implementation stuff 
}; 

// Generic arithmetic promotion template 
template <class T, class U> 
struct ArithmeticPromotion { 
    typedef typename X type; // I realize this is incorrect, but the point is it would 
           // figure out what X would be via trait testing, etc 
}; 

// Specialization of arithmetic promotion template 
template <> 
class ArithmeticPromotion<long long, unsigned long> { 
    typedef typename unsigned long long type; 
} 

// Arithmetic promotion template actually being used 
template <class T, class U> 
Complex<typename ArithmeticPromotion<T, U>::type> 
operator+ (Complex<T>& lhs, Complex<U>& rhs) { 
    return Complex<typename ArithmeticPromotion<T, U>::type>(lhs.r + rhs.r, lhs.i + rhs.i); 
} 

, daha fazla veya daha az aynı promosyon kuralları kendilerine uygulanan ile ilkeller iseniz olarak kullanıcı türleri tanımlanmış tedavi edebilir. Yani, sanırım sahip olduğum soru bu yararlı olabilecek bir şey mi olacak? Ve eğer öyleyse, ne tür ortak görevler kullanım kolaylığı için şablonlandırılmak istersiniz? Sadece tanıtım şablonlarının tek başına pratikte benimsenmesi için yeterli olmayacağı varsayımı üzerinde çalışıyorum.

Boost, matematik/araç/tanıtım başlığında benzer bir şeye sahiptir, ancak standart C matematik işlevlerine (2 inç veya 2 çift olarak beklenir) değerlerin aktarılmaya hazır hale getirilmesi için gerçekten daha fazla değer vardır ve tümünü atlar. İntegral türleri. Nesnelerinizin nasıl dönüştürüldüğünü tam olarak kontrol etmeyi tercih eden bir şey mi?

TL; DR: Promosyonun kendisini yapan makinelerin ötesinde aritmetik bir tanıtım başlığında ne tür yardım şablonlarını bulmayı bekliyorsunuz?

+0

Ben de herhalde çok sen (farklı türden) bazı değişkene güzel terfi sonucu atamak için gidiyoruz sona yüzden bunun için çok pratik bir kullanım göremiyorum. Belki de C++ 0x ile ** auto **, ama sonra da ** decltype ** işin çoğunu daha kolay halledebileceğini düşünüyorum. – UncleBens

+0

Görünüşe göre, bu, C++ 0x'da CommonType yapısı olarak uygulanacak ve doğru türün belirlenmesi için decltype'dan faydalanılacak. Birisi, standardın promosyon/dönüşüm hakkındaki kurallarına göre tamamen düzgün bir davranış isterse, sadece şablonların değişmeli varyasyonlarını yazmaları gerekir. – BenTrofatter

cevap

2

Bu kesinlikle yararlıdır - bu tür nesneleri, ara değerlerin doğru bir şekilde ifadeler halinde yazılması için üzerinde çalıştığım matematik kitaplığında kullanırız. Örneğin, bir templated toplama operatörünü sahip olabilir:

template<typename Atype, typename Btype> 
type_promote<Atype, Btype>::type operator+(Atype A, Btype B); 

Bu şekilde, farklı argüman türlerini idare edecek bir genel operatörü yazabilir ve içinde hassas kaybını önlemek için uygun türde bir değeri döndürecektir İçinde göründüğü ifade. Bu işleçlerde iç değişkenleri doğru şekilde bildirmek için de yararlıdır (vektör toplamları gibi).

Bunlarla ne yapmamız gerektiği sorusuyla ilgili olarak: Kaynak kodumuzu yalnızca tanımladığımız yere baktım ve tek sahip olduğumuz sadece tanımladığınız basit ArithmeticPromotion bildirimi - kompleksi çözmek için üç genel sürüm - gerçek, gerçek olanları kullanan gerçek-karmaşık, gerçek-gerçek ve gerçek-karmaşık varyantlar ve daha sonra gerçek-gerçek olanların bir listesi - hepsi yaklaşık 50 satırlık kod. Onlarla herhangi bir başka yardım şablonumuz yok ve bizim kullandığımızdan doğal olarak kullandığımız gibi değil.

(FWIW, bunu kendiniz yazmak istemiyorsanız, kaynağımızı http://www.codesourcery.com/vsiplplusplus/2.2/download.html numaralı telefondan indirin ve src/vsip/core/promote.hpp'dan çekin. Bu, BSD lisanslı kütüphanemizin parçası olsa bile, aslında bunu söylemez Dosyanın kendisinde.)

12

Bunun için kullanabileceğiniz, ?: işlecidir. Size iki tip arasında ortak tip verecek. İlk olarak, eğer iki tür aynıysa, siz iyisiniz. Ardından, türler farklılık gösteriyorsa, ?:'u çağırırsınız ve geri döndüğünüz türünü görürsünüz.

Farklı türdeki bu tür işlenenlerden birine uygulandığından, desteklenmeyen türler char, short ve bunların imzasız/imzalı sürümleri için özel durumlara ihtiyacınız vardır, sonuç bunların ikisi de olmayacaktır. Ayrıca, iki sınıfın tanıtılan aritmetik türlere dönüştürülebileceği duruma dikkat etmeniz gerekir. Bu hakkı elde etmek için, ?: sonucunun yükseltilmiş bir aritmetik tür olup olmadığını kontrol edin (madde 13.6'un ruhuna göre) ve o türü kullanın.

// typedef eiher to A or B, depending on what integer is passed 
template<int, typename A, typename B> 
struct cond; 

#define CCASE(N, typed) \ 
    template<typename A, typename B> \ 
    struct cond<N, A, B> { \ 
    typedef typed type; \ 
    } 

CCASE(1, A); CCASE(2, B); 
CCASE(3, int); CCASE(4, unsigned int); 
CCASE(5, long); CCASE(6, unsigned long); 
CCASE(7, float); CCASE(8, double); 
CCASE(9, long double); 

#undef CCASE 

// for a better syntax... 
template<typename T> struct identity { typedef T type; }; 

// different type => figure out common type 
template<typename A, typename B> 
struct promote { 
private: 
    static A a; 
    static B b; 

    // in case A or B is a promoted arithmetic type, the template 
    // will make it less preferred than the nontemplates below 
    template<typename T> 
    static identity<char[1]>::type &check(A, T); 
    template<typename T> 
    static identity<char[2]>::type &check(B, T); 

    // "promoted arithmetic types" 
    static identity<char[3]>::type &check(int, int); 
    static identity<char[4]>::type &check(unsigned int, int); 
    static identity<char[5]>::type &check(long, int); 
    static identity<char[6]>::type &check(unsigned long, int); 
    static identity<char[7]>::type &check(float, int); 
    static identity<char[8]>::type &check(double, int); 
    static identity<char[9]>::type &check(long double, int); 

public: 
    typedef typename cond<sizeof check(0 ? a : b, 0), A, B>::type 
    type; 
}; 

// same type => finished 
template<typename A> 
struct promote<A, A> { 
    typedef A type; 
}; 

sizin Complex<T> türü birbirlerini dönüştürülebilir ise ?: ortak bir türü bulamazlar.

template<typename T, typename U> 
struct promote<Complex<T>, Complex<U>> { 
    typedef Complex<typename promote<T, U>::type> type; 
}; 

Kullanımı basit:: gerçek dünya kullandığı için, en iyi birkaç yakalamak gerektiğini

int main() { 
    promote<char, short>::type a; 
    int *p0 = &a; 

    promote<float, double>::type b; 
    double *p1 = &b; 

    promote<char*, string>::type c; 
    string *p2 = &c; 
} 

Not İki Complex<T> ortak bir türü anlamaya nasıl anlatmak için promote uzmanlaşmak olabilir vakalar Ben örneğin <const int, int> için <T, T> benzer ele alınmalıdır, basitlik için dışarı sol (en iyi ilk şerit const veiçin volatile ve T* için T[N] dönüştürmek ve ve daha sonra, gerçek promote şablonuna delege verin - her ikisi de A için boost::remove_cv<boost::decay<T>>::type ve bunları temsil etmeden önce B yapın. Bunu yapmazsanız, check numaralı çağrı, bu durumlar için bir belirsizlik içinde sonuçlanacaktır.