İşlev şablonları için varsayılan şablon bağımsız değişkenleri


187

Varsayılan şablon bağımsız değişkenlerine neden yalnızca sınıf şablonlarında izin verilir? Üye işlev şablonunda neden varsayılan tür tanımlayamıyoruz? Örneğin:

struct mycclass {
  template<class T=int>
  void mymember(T* vec) {
    // ...
  }
};

Bunun yerine, C ++ varsayılan şablon bağımsız değişkenlerine yalnızca sınıf şablonunda izin verilmesini zorlar.


8
+1 Bu gerçekten zor bir soru.
AraK

1
İlk gönderilen üç cevap için şu örneği göz önünde bulundurun: struct S { template <class R = int> R get_me_R() { return R(); } };template parametresi bağlamdan çıkarılamaz.
AraK

3
İyi soru. 3 kişi "mantıklı değil" demek için cevap verdi ve genel olarak hepsi yanlış. İşlev şablonu parametrelerdir değil her zaman işlev çağrısı parametrelerinden düşülebilir. Örneğin, izin verildiyse yazabilirim template <int N = 1> int &increment(int &i) { i += N; return i; }ve sonra increment(i);ya increment<2>(i);. Olduğu gibi yazmak zorundayım increment<1>(i);.
Steve Jessop

Aslında, hem maden hem de AraK'ın örnekleri aşırı yüklenmeyle ele alınabilir. şablon parametresi çünkü LITB da, bilemiyorum, düşünemiyorum olabilir çıkarılabilir veya belirlenmiş olabilir.
Steve Jessop

3
@Steve: Eksik noktalı virgül aslında 1 Nisan 1992'de B. Stavtrup'un "C ++ Boşluğunun Aşırı Yüklenmesi" dergisini yayınladı ve 1 Nisan 1992'de yayımlandı. paper.html )

Yanıtlar:


148

Varsayılan şablon bağımsız değişkenleri vermek mantıklıdır. Örneğin bir sıralama işlevi oluşturabilirsiniz:

template<typename Iterator, 
         typename Comp = std::less<
            typename std::iterator_traits<Iterator>::value_type> >
void sort(Iterator beg, Iterator end, Comp c = Comp()) {
  ...
}

C ++ 0x onları C ++ ile tanıştırır. Bjarne Stroustrup'un bu hata raporuna bakın: İşlev Şablonları için Varsayılan Şablon Bağımsız Değişkenleri ve söyledikleri

Fonksiyon şablonları için varsayılan şablon argümanlarının yasaklanması, bağımsız fonksiyonların ikinci sınıf vatandaş olarak kabul edildiği ve tüm şablon argümanlarının belirtilmek yerine fonksiyon argümanlarından çıkarılması gereken zamanın yanlış anlaşılmış bir kalıntısıdır.

Kısıtlama, gereksiz işlevleri üye işlevlerinden farklı şekilde gereksiz hale getirerek programlama stilini ciddi şekilde sıkıştırır, böylece STL stili kod yazmayı zorlaştırır.


@Arman, kusur raporu bağlantısı C ++ 0x için çalışma taslağında yapılan değişiklikleri ve tartışmaları içerir. Çıkarılmayan veya açıkça belirtilmeyen bağımsız değişkenler varsayılan bağımsız değişkenlerden elde edilir. GCC4.4, C ++ 0x modundaki işlev şablonları için varsayılan bağımsız değişkenleri destekler.
Johannes Schaub - litb

4
Soru ya da cevapla ilgisi yok, ancak Herb Sutter geçen cumartesi toplantısı sonrasında yükselen standart C ++ 11'i aradı. Ben sadece bugün okudum ve paylaşmak gibi hissediyorum :) otlarutter.wordpress.com/2010/03/13/…
David Rodríguez - dribeas

ve zorunlu takip sorusu ... bunun ne zaman diğer derleyicilere girmesi bekleniyor :)
Jamie Cook

@ JohannesSchaub-litb Aynı sorun vardı: bir şablon işlevinde varsayılan türü speficy imkanı yok. Ben varsayılan tip ( doublebenim durumumda) fonksiyonun açık bir örnekleme ile çözdüm . Belki de "genel" değil, ama bu uygulamanın bir dezavantajı var mı? Teşekkürler.
JackOLantern

Aşağıdaki kod, error: invalid conversion from ‘int’ to ‘int*’neden olduğu gibi bir hatayla derlenemez : `#include <array> #include <algoritma> #include <fonksiyonel> şablon <typename Iterator, typename Comp = std :: less <Iterator>> void my_sort ( Yineleyici dilenci, Yineleyici sonu, Comp c = Comp ()) {std :: sort (dilenmek, bitirmek, c); int main () {std :: dizi <int, 5> ar {5,2,21,7,4}; my_sort (ar.begin (), ar.end ()); } `
Luke Peterson

36

Alıntı C ++ şablonları: Complete Guide (sayfa 207):

Şablonlar başlangıçta C ++ diline eklendiğinde, açık işlev şablonu bağımsız değişkenleri geçerli bir yapı değildi. İşlev şablonu bağımsız değişkenleri her zaman çağrı ifadesinden çıkarılmalıdır. Sonuç olarak, varsayılan işlev şablonu bağımsız değişkenlerine izin vermek için zorlayıcı bir neden görünmüyordu, çünkü varsayılan her zaman çıkarılan değer tarafından geçersiz kılınır.


basit ve özlü :)
InQusitive

17

Şimdiye kadar, işlev şablonları için varsayılan şablon parametrelerinin tüm istenen örnekleri aşırı yüklerle yapılabilir.

arak:

struct S { 
    template <class R = int> R get_me_R() { return R(); } 
};

olabilirdi:

struct S {
    template <class R> R get_me_R() { return R(); } 
    int get_me_R() { return int(); }
};

Benim:

template <int N = 1> int &increment(int &i) { i += N; return i; }

olabilirdi:

template <int N> int &increment(int &i) { i += N; return i; }
int &increment(int &i) { return increment<1>(i); }

LITB:

template<typename Iterator, typename Comp = std::less<Iterator> >
void sort(Iterator beg, Iterator end, Comp c = Comp())

olabilirdi:

template<typename Iterator>
void sort(Iterator beg, Iterator end, std::less<Iterator> c = std::less<Iterator>())

template<typename Iterator, typename Comp >
void sort(Iterator beg, Iterator end, Comp c = Comp())

Stroustrup:

template <class T, class U = double>
void f(T t = 0, U u = 0);

Olabilirdi:

template <typename S, typename T> void f(S s = 0, T t = 0);
template <typename S> void f(S s = 0, double t = 0);

Hangi aşağıdaki kod ile kanıtladı:

#include <iostream>
#include <string>
#include <sstream>
#include <ctype.h>

template <typename T> T prettify(T t) { return t; }
std::string prettify(char c) { 
    std::stringstream ss;
    if (isprint((unsigned char)c)) {
        ss << "'" << c << "'";
    } else {
        ss << (int)c;
    }
    return ss.str();
}

template <typename S, typename T> void g(S s, T t){
    std::cout << "f<" << typeid(S).name() << "," << typeid(T).name()
        << ">(" << s << "," << prettify(t) << ")\n";
}


template <typename S, typename T> void f(S s = 0, T t = 0){
    g<S,T>(s,t);
}

template <typename S> void f(S s = 0, double t = 0) {
    g<S,double>(s, t);
}

int main() {
        f(1, 'c');         // f<int,char>(1,'c')
        f(1);              // f<int,double>(1,0)
//        f();               // error: T cannot be deduced
        f<int>();          // f<int,double>(0,0)
        f<int,char>();     // f<int,char>(0,0)
}

Yazdırılan çıktı, f'ye yapılan her çağrı için yorumları eşleştirir ve yorum yapılan çağrı beklendiği gibi derlenemez.

Bu nedenle, varsayılan şablon parametrelerinin "gerekli olmadığından", ancak muhtemelen yalnızca varsayılan işlev bağımsız değişkenlerinin "gerekli olmadığından" aynı olduğundan şüpheleniyorum. Stroustrup'un hata raporunun da belirttiği gibi, çıkarılmamış parametrelerin eklenmesi, herkesin varsayılanları yararlı hale getirdiğini fark etmesi ve / veya gerçekten takdir etmesi için çok geç oldu. Dolayısıyla mevcut durum, hiçbir zaman standart olmayan fonksiyon şablonlarının bir versiyonuna dayanmaktadır.


@Steve: Yani yumurta tavuktan daha hızlı koşuyordu? :) ilginç. Teşekkürler.
Arman

1
Muhtemelen bunlardan sadece biri. C ++ standardizasyon süreci kısmen yavaş çalışır, böylece bir değişiklik standardın başka bir yerinde fırsatlar veya zorluklar yarattığında fark edecek zamana sahiptir. Beklentiler, bir çelişki ya da belirsizlik tespit ettiklerinde, taslak standardı uygulayan insanlar tarafından zorluklar yakalar. Daha önce izin verilmeyen şeylere izin verme fırsatları, artık yasa dışı olması gerekmediğini fark eden kodu yazmak isteyen birine güveniyor ...
Steve Jessop

2
Bir tane sana daha: template<typename T = void> int SomeFunction();. Buradaki template parametresi asla kullanılmaz ve aslında fonksiyon asla çağrılmaz; atıfta bulunduğu tek yer bir decltypeveya sizeof. Ad kasıtlı olarak başka bir işlevin adıyla eşleşir, ancak bir şablon olması, derleyicinin varsa ücretsiz işlevi tercih edeceği anlamına gelir. İkisi SFINAE içinde bir işlev tanımının eksik olduğu varsayılan davranışı sağlamak için kullanılır.
Tom

4

Windows'ta, Visual Studio'nun tüm sürümleriyle bu hatayı ( C4519 ) bir uyarıya dönüştürebilir veya şu şekilde devre dışı bırakabilirsiniz:

#ifdef  _MSC_VER
#pragma warning(1 : 4519) // convert error C4519 to warning
// #pragma warning(disable : 4519) // disable error C4519
#endif

Daha fazla ayrıntıyı burada görebilirsiniz .


1
Bu, "varsayılan şablon bağımsız değişkenlerine yalnızca sınıf şablonunda izin verilir" iletisini devre dışı bıraksa da, aslında şablon örnekleme işleminin sağlanan değeri kullanmasını sağlamaz . Bu VS2013 (veya C ++ 11 hatasını tamamlamış başka bir derleyici gerektirir 226 "İşlev şablonları için varsayılan şablon bağımsız değişkenleri")
puetzk

1

Ne kullanacağım bir sonraki hile:

Diyelim ki böyle bir işleve sahip olmak istiyorsunuz:

template <typename E, typename ARR_E = MyArray_t<E> > void doStuff(ARR_E array)
{
    E one(1);
    array.add( one );
}

İzin verilmeyecek, ama bir sonraki yolu yapacağım:

template <typename T>
struct MyArray_t {
void add(T i) 
{
    // ...
}
};

template <typename E, typename ARR_E = MyArray_t<E> >
class worker {
public:
    /*static - as you wish */ ARR_E* parr_;
    void doStuff(); /* do not make this one static also, MSVC complains */
};

template <typename E, typename ARR_E>
void worker<E, ARR_E>::doStuff()
{
    E one(1);
    parr_->add( one );
}

Böylece bu şekilde kullanabilirsiniz:

MyArray_t<int> my_array;
worker<int> w;
w.parr_ = &arr;
w.doStuff();

Gördüğümüz gibi açıkça ikinci parametreyi ayarlamaya gerek yok. Belki biri için yararlı olacaktır.


Bu kesinlikle bir cevap değil.
Köpek yavrusu

@deadmg - nedenini açıklayabilir misin? Hepimiz C ++ şablon gurusu değiliz. Teşekkürler.
Kev

Bu oldukça düzgün bir çözümdür, ancak istediğiniz tüm durumları kapsamaz. Örneğin, bunu bir kurucuya nasıl uygularsınız?
Tiberiu Savin

@TiberiuSavin - eğer seni doğru anladıysam, bunu şöyle yapabilirsin: şablon <typename E, ARR_E> typename <E, ARR_E> :: worker (ARR_E * parr) {parr_ = parr; }. Ve sonra şu şekilde kullanın: worker <int> w2 (& my_array);
alariq
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.