Şablon sınıfında C ++ 20 sınıf dışı tanım


12

C ++ C ++ 20 standardına kadar, bir şablon sınıfının bazı özel üyelerini kullanan sınıf dışı bir operatör tanımlamak istediğimizde, buna benzer bir yapı kullanırdık:

template <typename T>
class Foo;

template <typename T>
constexpr bool operator==(T lhs, const Foo<T>& rhs);

template <typename T>
class Foo {
public:
    constexpr Foo(T k) : mK(k) {}

    constexpr friend bool operator==<T>(T lhs, const Foo& rhs);

private:
    T mK;
};

template <typename T>
constexpr bool operator==(T lhs, const Foo<T>& rhs) {
    return lhs == rhs.mK;
}

int main() {
    return 1 == Foo<int>(1) ? 0 : 1;
}

Bununla birlikte, C ++ 20'den beri, sınıf dışı bildirimi, dolayısıyla ileri bildirimi atlayabiliriz, böylece sadece:

template <typename T>
class Foo {
public:
    constexpr Foo(T k) : mK(k) {}

    constexpr friend bool operator==<T>(T lhs, const Foo& rhs);
private:
    T mK;
};

template <typename T>
constexpr bool operator==(T lhs, const Foo<T>& rhs) {
    return lhs == rhs.mK;
}

Demo

Şimdi sorum şu: C ++ 20'nin hangi kısmı bunu yapmamıza izin veriyor? Ve bu daha önceki C ++ standartlarında neden mümkün olmadı?


Yorumlarda belirtildiği gibi, clang demoda sunulan bu kodu kabul etmiyor, bu aslında gcc'de bir hata olabileceğini düşündürüyor.

Gcc'nin bugzilla'sına bir hata raporu verdim


2
Ben şahsen sınıf tanımı tercih, şablon fonksiyonu kaçınarak (ve kesinti "sorunları" (için eşleşme yok "c string" == Foo<std::string>("foo"))).
— Jarod42

@ Jarod42 Tamamen katılıyorum, sınıf içi tanımlamayı da tercih ediyorum. Sadece C ++ 20, uygulamanın gizli bir .inl dosyasında olduğu genel bir API'de yararlı olabilecek, sınıfın ouf-sınıfını tanımlarken işlev imzasını üç kez tekrarlamamıza izin verdiğini bulmak beni şaşırttı.
— ProXicT

İmkansız olduğunu fark etmedim. Neden şimdiye kadar sorunsuz bir şekilde kullandım?
— ALX23z

1
Hmmm, geçici olarak , çok fazla değişmedi, özellikle bu davranıştan sorumlu olması gereken 1.3 değil . Clang gelmez yana değil kodunuzu kabul, ben bir hata sahip gcc doğru eğilerek.
— n314159

@ ALX23z Sınıf geçici değilse, sınıf dışı bildirimi olmadan çalışır.
— ProXicT

Yanıtlar:


2

GCC'nin bir hatası var.

İsim araması, <söz konusu ad bir (arkadaş, açık uzmanlaşma veya açık örnekleme) bildiriminde bildirilen ad olsa bile, a'dan önce görünen şablon adları için her zaman gerçekleştirilir .

Adı Çünkü operator==arkadaşı bildiriminde bir vasıfsız adıdır ve bir şablona arama isim tabidir, iki fazlı adım arama kurallar geçerlidir. Bu bağlamda, operator==bağımlı bir ad değildir (bir işlev çağrısının parçası değildir, bu nedenle ADL uygulanmaz), bu nedenle ad, göründüğü noktaya bakılır ve bağlanır (bkz. [Temp.nondep] paragraf 1). Örneğiniz kötü biçimlendirilmiş, çünkü bu isim araması hiçbir bildirim bulamıyor operator==.

GCC'nin P0846R0 nedeniyle bunu bir C ++ 20 modunda kabul etmesini beklerdim , bu da bir şablon olarak operator==<T>(a, b)önceden bildirimi olmasa bile (örneğin) bir şablonda kullanılmasına izin verir operator==.

İşte daha da ilginç bir test çantası:

template <typename T> struct Foo;

#ifdef WRONG_DECL
template <typename T> bool operator==(Foo<T> lhs, int); // #1
#endif

template <typename T> struct Foo {
  friend bool operator==<T>(Foo<T> lhs, float); // #2
};

template <typename T> bool operator==(Foo<T> lhs, float); // #3
Foo<int> f;

İle -DWRONG_DECL, GCC ve Clang bu programın kötü biçimlendirildiğini kabul ederler: 2 numaralı arkadaş bildirimi için niteliksiz arama, şablon tanımı bağlamında, 1 numaralı bildirimi bulur ve bu, örneklenen arkadaşla eşleşmez Foo<int>. 3 numaralı bildirim bile dikkate alınmaz, çünkü şablondaki kalifiye olmayan arama bunu bulamaz.

İle -UWRONG_DECLniteliksiz arama için: GCC (C ++ 17'de ve öncesi) ve Clang, bu program farklı bir nedenden dolayı kötü oluşur kabul operator==on line 2. bulur şey.

Ancak -UWRONG_DECL, C ++ 20 modundaki GCC, operator==# 2'deki kalifiye olmayan aramanın başarısız olduğuna karar veriyor gibi görünüyor (muhtemelen P0846R0 nedeniyle) ve ardından şimdi # 3 bularak şablon örnekleme içeriğinden aramayı yeniden yapıyor gibi görünüyor. şablonlar için normal iki aşamalı ad arama kuralının ihlali.


Bu ayrıntılı açıklama için teşekkürler, çok iyi koymak!
— ProXicT
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.