Ş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.