Kafa karıştırıcı Şablon hatası


91

Bir süredir clang ile oynuyorum ve bir şablon hatasından kurtarmak için ipuçları sağlaması beklenen "test / SemaTemplate / bağımlı-şablon-recovery.cpp" (clang dağıtımında) ile karşılaştım.

Her şey kolayca asgari bir örneğe indirgenebilir:

template<typename T, typename U, int N> struct X {
    void f(T* t)
    {
        // expected-error{{use 'template' keyword to treat 'f0' as a dependent template name}}
        t->f0<U>();
    }
};

Clang tarafından verilen hata mesajı:

tpl.cpp:6:13: error: use 'template' keyword to treat 'f0' as a dependent template name
         t->f0<U>();
            ^
            template 
1 error generated.

... Ama templatekodun sözdizimsel olarak doğru olması için anahtar kelimeyi tam olarak nereye eklemesi gerektiğini anlamakta güçlük çekiyorum ?


11
Okun işaret ettiği yere yerleştirmeyi denediniz mi?
Mike Seymour

Yanıtlar:


104

ISO C ++ 03 14,2 / 4:

Bir üye şablon uzmanlığının adı sonra göründüğünde. veya -> bir sonek ifadesinde veya bir nitelenmiş kimlikteki yuvalanmış ad tanımlayıcısından sonra ve sonek ifadesi veya nitelenmiş kimlik açıkça bir şablon parametresine (14.6.2) bağlıdır , üye şablon adı anahtar kelime şablonunun önüne eklenir . Aksi takdirde, adın şablon olmayan bir ad olduğu varsayılır.

İçinde t->f0<U>(); f0<U>, sonradan görünen ->ve açıkça şablon parametresine bağlı olan bir üye şablon uzmanlığıdır U, bu nedenle üye şablonu uzmanlığı templateanahtar kelimeyle önek olarak eklenmelidir .

Yani değiştirmek t->f0<U>()için t->template f0<U>().


İlginç bir şekilde, ifadeyi parantez içine koymayı düşündüm: t->(f0<U>())bunu f0<U>()tek başına ifadeye koyacağını düşündüğüm için

24
Bunun neden böyle olduğu hakkında yorum yapabilir misiniz? C ++ neden bu tür bir sözdizimi gerektirir?
Meraklı

2
Evet bu tuhaf. Dil, şablon anahtar kelimesinin bulunması gerektiğini "algılayabilir". Eğer bunu yapabiliyorsa, anahtar kelimeyi sadece oraya "yerleştirmelidir".
Enrico Borba

26

Başkalarının belirttiği noktalara ek olarak, bazen derleyicinin kararını veremediğini ve her iki yorumun da örnekleme sırasında alternatif geçerli programlar sağlayabileceğini unutmayın.

#include <iostream>

template<typename T>
struct A {
  typedef int R();

  template<typename U>
  static U *f(int) { 
    return 0; 
  }

  static int f() { 
    return 0;
  }
};

template<typename T>
bool g() {
  A<T> a;
  return !(typename A<T>::R*)a.f<int()>(0);
}


int main() {
  std::cout << g<void>() << std::endl;
}

Bu 0, templateönceden çıkarıldığında f<int()>ancak 1yerleştirildiğinde yazdırılır . Bunu kodun ne yaptığını anlamak için bir alıştırma olarak bırakıyorum.


3
İşte bu şeytani bir örnek!
Matthieu M.

1
Visual Studio 2013'te tanımladığınız davranışı yeniden oluşturamıyorum. Her zaman çağırıyor f<U>ve yazdırıyor 1, bu da benim için çok mantıklı. Hala templateanahtar kelimenin neden gerekli olduğunu ve ne gibi bir fark yarattığını anlamıyorum.
Violet Zürafa

@Violet the VSC ++ derleyicisi uyumlu bir C ++ derleyicisi değildir. VSC ++ 'nın neden her zaman 1. yazdırdığını bilmek istiyorsanız yeni bir soru gerekir.
Johannes Schaub - litb

1
Bu cevap, neden templategerekli olduğunu açıklıyor : stackoverflow.com/questions/610245/… , yalnızca anlaşılması zor olan standart terimlere güvenmeden. Bu yanıttaki herhangi bir şey hala kafa karıştırıcıysa lütfen bildirin.
Johannes Schaub -

@ JohannesSchaub-litb: Teşekkürler, harika bir yanıt. Görünüşe göre, daha önce okudum çünkü zaten benim tarafımdan oy almıştı. Görünüşe göre hafızam meh.
Violet Giraffe

12

İmlecin olduğu noktanın hemen önüne ekleyin:

template<typename T, typename U, int N> struct X {
     void f(T* t)
     {
        t->template f0<U>();
     }
};

Düzenleme: Bir derleyici gibi düşünürseniz, bu kuralın nedeni daha net hale gelir. Derleyiciler genellikle yalnızca bir veya iki simgenin ilerisine bakar ve genellikle ifadenin geri kalanına "ileriye bakmaz". [Düzenle: açıklamaya bakın] Anahtar kelimenin nedeni typename, bağımlı tür adlarını belirtmek için anahtar kelimeye ihtiyaç duymanızın nedeniyle aynıdır : derleyiciye "hey, göreceğiniz tanımlayıcı bir şablonun adıdır, değil statik veri üyesinin adı ve ardından küçüktür işareti ".


1
Bunu asla tahmin edemezdim ... ama teşekkürler ;-). açıkça her zaman C ++ hakkında öğrenilecek bir şeyler vardır!

3
Sonsuz ileriye baksanız bile, yine de ihtiyacınız olacak template. Hem olan hem templatede olmayanların farklı davranışlara sahip geçerli programlar üreteceği durumlar vardır. Yani bu sadece sözdizimsel bir problem değildir ( t->f0<int()>(0)sözdizimsel olarak hem küçüktür hem de şablon argüman listesi versiyonu için geçerlidir).
Johannes Schaub -

@Johannes Schaub - litb: Doğru, yani ileriye bakmaktan çok ifadeye tutarlı anlamsal anlam atama problemi.
Doug

11

C ++ Şablonlarından Alıntı

.Template Construct Typename tanıtıldıktan sonra çok benzer bir problem keşfedildi. Standart bit kümesi türünü kullanarak aşağıdaki örneği düşünün:

template<int N> 
void printBitset (std::bitset<N> const& bs) 
{ 
    std::cout << bs.template to_string<char,char_traits<char>, 
                                       allocator<char> >(); 
} 

Bu örnekteki garip yapı .template'dir. Bu fazladan şablon kullanımı olmadan, derleyici, takip eden belirtecin (<) gerçekten "küçük" olmadığını, ancak bir şablon bağımsız değişken listesinin başlangıcı olduğunu bilmez. Bunun yalnızca dönemden önceki yapı bir şablon parametresine bağlı olması durumunda bir sorun olduğunu unutmayın. Örneğimizde, bs parametresi, şablon parametresi N'ye bağlıdır.

Sonuç olarak, .template gösterimi (ve -> şablon gibi benzer gösterimler) yalnızca şablonların içinde ve yalnızca bir şablon parametresine bağlı olan bir şeyi izliyorlarsa kullanılmalıdır.

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.