`Void_t` nasıl çalışır


149

Walter Brown'un Cppcon14'te SFINAE tekniğini sunduğu modern şablon programlama ( Bölüm I , Bölüm II ) hakkındaki konuşmasını void_tizledim.

Örnek: Tüm şablon argümanlarının iyi biçimlendirilmiş olup
olmadığını değerlendiren basit bir değişken şablonu verildiğinde void:

template< class ... > using void_t = void;

ve aşağıdaki özellik olduğunu üyesi değişkeni denilen varlığını denetler üyesi :

template< class , class = void >
struct has_member : std::false_type
{ };

// specialized as has_member< T , void > or discarded (sfinae)
template< class T >
struct has_member< T , void_t< decltype( T::member ) > > : std::true_type
{ };

Bunun neden ve nasıl çalıştığını anlamaya çalıştım. Bu nedenle küçük bir örnek:

class A {
public:
    int member;
};

class B {
};

static_assert( has_member< A >::value , "A" );
static_assert( has_member< B >::value , "B" );

1. has_member< A >

  • has_member< A , void_t< decltype( A::member ) > >
    • A::member var
    • decltype( A::member ) iyi biçimlendirilmiş
    • void_t<> geçerlidir ve void
  • has_member< A , void > ve bu nedenle özel şablonu seçer
  • has_member< T , void > ve değerlendirir true_type

2. has_member< B >

  • has_member< B , void_t< decltype( B::member ) > >
    • B::member mevcut değil
    • decltype( B::member ) kötü biçimlendirilmiş ve sessizce başarısız (sfinae)
    • has_member< B , expression-sfinae > yani bu şablon atılır
  • derleyici has_member< B , class = void >varsayılan argüman olarak void ile bulur
  • has_member< B > için değerlendirir false_type

http://ideone.com/HCTlBb

Sorular:
1. Bu anlayışım doğru mu?
2. Walter Brown, varsayılan argümanın çalışması için kullanılanla aynı tipte olması gerektiğini belirtir void_t. Neden? (Bu türlerin neden eşleşmesi gerektiğini anlamıyorum, iş sadece varsayılan bir tür değil mi?)


6
Reklam 2) Statik assert olarak yazılmıştır düşünün: has_member<A,int>::value. Ardından, değerlendirilen kısmi uzmanlık has_member<A,void>eşleşemez. Bu nedenle, has_member<A,void>::valuesözdizimsel şekerle varsayılan bir tür argümanı olması gerekir void.
dyp

1
@dyp Teşekkürler, bunu düzenleyeceğim. Mh, henüz has_member< T , class = void >temerrüde düşme ihtiyacım voidyok. Bu özelliğin herhangi bir anda yalnızca 1 şablon bağımsız değişkeni ile kullanılacağını varsayarsak, varsayılan bağımsız değişken herhangi bir tür olabilir mi?
Aralık'ta saçmalık

İlginç soru.
AStopher

2
Bu öneri, içinde o Not open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4436.pdf Walter değişti template <class, class = void>etmek template <class, class = void_t<>>. Şimdi void_ttakma ad şablon uygulamasıyla istediğimizi yapmakta
özgürüz

Yanıtlar:


133

1. Birincil Sınıf Şablonu

Yazdığınızda has_member<A>::value, derleyici adı arar has_memberve birincil sınıf şablonunu, yani bu bildirimi bulur :

template< class , class = void >
struct has_member;

(OP'de bu bir tanım olarak yazılmıştır.)

Şablon bağımsız değişken listesi <A>, bu birincil şablonun şablon parametre listesiyle karşılaştırılır. Birincil şablon iki parametresi vardır, ama sadece birisi verilen beri kalan parametre varsayılan şablon argümanı varsayılan ayarı: void. Sanki yazmışsın gibi has_member<A, void>::value.

2. Uzman Sınıf Şablonu

Artık , şablon parametre listesi, şablondaki tüm uzmanlıklarla karşılaştırılır has_member. Yalnızca uzmanlık eşleşmezse, birincil şablonun tanımı geri dönüş olarak kullanılır. Kısmi uzmanlaşma dikkate alınır:

template< class T >
struct has_member< T , void_t< decltype( T::member ) > > : true_type
{ };

Derleyici, şablon bağımsız değişkenlerini A, voidkısmi uzmanlıkta tanımlanan kalıplarla eşleştirmeye çalışır : Tve void_t<..>tek tek. İlk olarak , şablon argümanı kesinti yapılır. Yukarıdaki kısmi uzmanlaşma hala argümanlarla "doldurulması" gereken şablon parametrelerine sahip bir şablondur.

İlk kalıp T , derleyicinin template-parametresini çıkarmasına izin verir T. Bu önemsiz bir kesinti, ama T const&yine de çıkarabileceğimiz bir modeli düşünün T. Örüntü Tve şablon argümanı için olduğu Asonucuna varıyoruz .TA

İkinci modelde void_t< decltype( T::member ) > , template-parametresi T, herhangi bir şablon argümanından çıkarılamayacağı bir bağlamda görünür.

Bunun iki nedeni var:

  • İçindeki ifade decltype, şablon bağımsız değişken kesintisinden açıkça hariç tutulur. Sanırım bu keyfi olarak karmaşık olabileceğinden.

  • Onsuz bir desen kullanılmış olsa bile decltypebenzeri void_t< T >birleştirdikten sonra kesinti Tçözülmesi takma şablonu olur. Yani, takma ad şablonunu çözüyoruz ve daha sonra türü Tsonuç deseninden çıkarmaya çalışıyoruz . Bununla birlikte, sonuçta ortaya çıkan örüntü, voidbağımlı değildir Tve bu nedenle belirli bir tip bulmamıza izin vermez T. Bu, sabit bir işlevi tersine çevirmeye çalışmanın matematiksel problemine benzer (bu terimlerin matematiksel anlamında).

Şablon argümanı indirimi bittikten (*) , şimdi çıkar sanan şablon argümanları ikame edilir. Bu, şuna benzer bir uzmanlık oluşturur:

template<>
struct has_member< A, void_t< decltype( A::member ) > > : true_type
{ };

Artık tür void_t< decltype( A::member ) >değerlendirilebilir. İkame işleminden sonra iyi biçimlendirilmiştir, bu nedenle İkame Hatası oluşmaz. Biz:

template<>
struct has_member<A, void> : true_type
{ };

3. Seçim

Şimdi , bu uzmanlığın şablon parametre listesini orijinaline sağlanan şablon argümanlarıyla karşılaştırabiliriz has_member<A>::value. Her iki tip de tam olarak eşleşir, bu nedenle bu kısmi uzmanlık seçilir.


Öte yandan, şablonu şöyle tanımladığımızda:

template< class , class = int > // <-- int here instead of void
struct has_member : false_type
{ };

template< class T >
struct has_member< T , void_t< decltype( T::member ) > > : true_type
{ };

Aynı uzmanlıkla sonuçlanıyoruz:

template<>
struct has_member<A, void> : true_type
{ };

ancak has_member<A>::valueşimdilik şablon argüman listemiz <A, int>. Bağımsız değişkenler uzmanlığın parametreleriyle eşleşmiyor ve birincil şablon bir geri dönüş olarak seçiliyor.


(*) Standart, IMHO kafa karıştırıcı bir şekilde, ikame sürecini ve şablon argüman kesinti işleminde açıkça belirtilen şablon argümanlarının eşleşmesini içerir . Örneğin (N4296 sonrası) [temp.class.spec.match] / 2:

Kısmi uzmanlığın şablon argümanları asıl şablon argüman listesinden çıkartılabiliyorsa, kısmi bir uzmanlık, verilen bir gerçek şablon argüman listesiyle eşleşir.

Ancak bu sadece kısmi uzmanlaşmanın tüm şablon parametrelerinin çıkarılması gerektiği anlamına gelmez; ayrıca ikame işleminin başarılı olması gerektiği ve (göründüğü gibi?) şablon argümanlarının kısmi uzmanlığın (ikame edilmiş) şablon parametreleriyle eşleşmesi gerektiği anlamına gelir. Ben tamamen farkında değilim Not nerede Standart ikame argüman listesi ve sağlanan bağımsız değişken listesinin arasında karşılaştırma belirtir.


3
Teşekkür ederim! Tekrar tekrar okudum ve sanırım şablon argüman çıkarımının tam olarak nasıl çalıştığını ve derleyicinin son şablon için ne seçtiğini şu an doğru değil.
Aralık'ta saçmalık

1
@ JohannesSchaub-litb Teşekkürler! Yine de bu biraz iç karartıcı. Şablon argümanını bir uzmanlıkla eşleştirmek için gerçekten bir kural yok mu? Açık uzmanlıklar için bile değil mi?
dyp

2
W / r / t varsayılan şablon bağımsız değişkenleri, open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2008
TC

1
@dyp Birkaç hafta sonra ve bu konuda çok okuma ve bu pasaj bir ipucu ile ben bunun nasıl çalıştığını anlamaya başlar düşünüyorum. Açıklamanız bana okumaktan daha anlamlı geliyor, teşekkürler!
nonsensation

1
Eklemek istedim, birincil şablon terimi anahtar (şablonlar kodda ilk karşılaşma) anahtar oldu
saçmalık

18
// specialized as has_member< T , void > or discarded (sfinae)
template<class T>
struct has_member<T , void_t<decltype(T::member)>> : true_type
{ };

Yukarıdaki uzmanlık sadece iyi biçimlendirildiğinde ortaya çıkar, bu yüzden decltype( T::member )geçerli ve belirsiz değildir. uzmanlaşma has_member<T , void>yorumda durum olarak böyledir .

Yazdığınızda has_member<A>bunun has_member<A, void>nedeni varsayılan şablon bağımsız değişkenidir.

Ve uzmanlığımız var has_member<A, void>(bu yüzden devralın true_type) ama uzmanlığımız yok has_member<B, void>(bu yüzden varsayılan tanımı kullanıyoruz: devralma false_type)

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.