C ++ 20 Kavramlar: Şablon bağımsız değişkeni birden çok kavram için uygun olduğunda hangi şablon uzmanlığı seçilir?


23

Verilen:

#include <concepts>
#include <iostream>

template<class T>
struct wrapper;

template<std::signed_integral T>
struct wrapper<T>
{
    wrapper() = default;
    void print()
    {
        std::cout << "signed_integral" << std::endl;
    }
};

template<std::integral T>
struct wrapper<T>
{
    wrapper() = default;
    void print()
    {
        std::cout << "integral" << std::endl;
    }
};

int main()
{
    wrapper<int> w;
    w.print(); // Output : signed_integral
    return 0;
}

Yukarıdaki kod itibaren inthem nitelendirir std::integralve std::signed_integralkavram.

Şaşırtıcı bir şekilde bu, hem GCC hem de MSVC derleyicilerinde "işaretli_integral" derler ve yazdırır. Ben "şablon uzmanlık zaten tanımlanmış" satırlarında bir hata ile başarısız bekliyordum.

Tamam, bu yasal, yeterince adil, ama neden std::signed_integralyerine seçildi std::integral? Birden fazla kavram şablon argümanı için uygun olduğunda standartta hangi şablon uzmanlığının seçildiği ile tanımlanan herhangi bir kural var mı?


Derleyici (ler) in bunu kabul etmesi, özellikle de benimsemenin bu ilk aşamalarında yasal olduğunu söylemem.
Slava

@Slava bu durumda, kavramlar dikkatli bir şekilde tasarlanmıştır, böylece birbirlerini sezgisel bir şekilde ele
alırlar

@GuillaumeRacicot gayet iyi, ben sadece "yasal çünkü derleyici kabul etti" sonucunun yanıltıcı diyelim yorum yaptı. Ben bunun yasal olmadığını söylemedim.
Slava

Yanıtlar:


14

Bunun nedeni, kavramların, şablonun kendilerinin nasıl sıralandığı gibi, diğerlerinden daha uzman olabilmesidir. Buna kısıtlamaların kısmi sıralaması denir

Kavramlar söz konusu olduğunda, eşdeğer kısıtlamalar içerdiklerinde birbirlerini varsayarlar. Örneğin, nasıl std::integralve nasıl std::signed_integraluygulandığı aşağıda açıklanmıştır:

template<typename T>
concept integral = std::is_integral_v<T>;

template<typename T> //   v--------------v---- Using the contraint defined above
concept signed_integral = std::integral<T> && std::is_signed_v<T>;

Derleyicinin karşıt ifadeyi buna bağlayan kısıtlamaları normalleştirmek:

template<typename T>
concept integral = std::is_integral_v<T>;

template<typename T>
concept signed_integral = std::is_integral_v<T> && std::is_signed_v<T>;

Bu örnekte, tamamen signed_integralima edilmektedir integral. Yani bir anlamda, imzalı bir integral bir integralden "daha kısıtlıdır".

Standart bunu şöyle yazıyor:

Kaynaktan [temp.func.order] / 2 (vurgu benim):

Kısmi sıralama, her bir şablonu sırayla dönüştürerek (sonraki paragrafa bakın) ve işlev türünü kullanarak şablon bağımsız değişken kesinti gerçekleştirerek iki işlev şablonundan hangisinin diğerinden daha özel olduğunu seçer. Tümdengelim süreci, şablonlardan birinin diğerinden daha özel olup olmadığını belirler. Öyleyse, daha özel şablon kısmi sipariş süreci tarafından seçilen şablondur. Her iki kesinti de başarılı olursa, kısmi sıralama [temp.constr.order] içindeki kurallarda açıklandığı şekilde daha kısıtlanmış şablonu seçer .

Bu, bir şablon için birden fazla olası ikame varsa ve her ikisi de kısmi sıralamadan seçildiğinde, en kısıtlanmış şablonu seçeceği anlamına gelir.

Kaynaktan [temp.constr.order] / 1 :

Bir kısıtlama P kapsadığını bir kısıtlama Q , her ayırıcı maddesi için, ancak ve ancak, P i arasında ayırıcı normal formda P , P i her birleşik maddesi kapsadığını S j ve birleşik normal formda Q ,

  • Bir ayırıcı maddesi P ı bir birleşik maddesi kapsadığını S j atomik kısıtlama vardır, ancak ve ancak p ia içinde P ı olan bir atom kısıtlama vardır S JB içinde Q, j bu şekilde P iA kapsadığını S jb ve

  • bir atomik kısıtlama A , yalnızca A ve B [temp.constr.atomic] 'de açıklanan kuralları kullanarak özdeş ise ve başka bir atomik kısıtlamayı B yerine getirir .

Bu, derleyicinin sipariş kısıtlamaları ve dolayısıyla kavramlar için kullandığı toplam algoritmasını açıklar.


2
Bir paragrafın ortasında
iz sürüyor gibisin

11

C ++ 20, belirli bir kısıtlanmış varlığın diğerinden "daha kısıtlı" olduğuna karar verme mekanizmasına sahiptir. Bu basit bir şey değil.

Bu, bir kısıtlamayı atom bileşenlerine ayırma kavramı ile başlar; bu, kısıt normalizasyonu adı verilen bir süreçtir . Buraya girmek çok büyük ve karmaşık, ancak temel fikir, bir kısıtlamadaki her ifadenin, kavram olmayan bir bileşen alt ifadesine ulaşıncaya kadar özyinel olarak atomik kavramsal parçalarına ayrılmasıdır.

Öyleyse, integralve signed_integralkavramlarının nasıl tanımlandığına bakalım :

template<class T>
  concept integral = is_integral_v<T>;
template<class T>
  concept signed_­integral = integral<T> && is_signed_v<T>;

Ayrışması integralsadece is_integral_v. Ayrışması signed_integralDİR is_integral_v && is_signed_v.

Şimdi, kısıtlama toplamı kavramına geliyoruz . Bu biraz karmaşıktır, ancak temel fikir, eğer C1'in ayrışması C2'deki her alt ifadeyi içeriyorsa C1 kısıtlamasının C2 sınırını "tükettiği" söylenir. Biz görebilirsiniz integralsubsume vermez signed_integral, ama signed_integral yapar subsume integralher şey içerdiğinden, integralyapar.

Ardından, kısıtlı varlıklar sipariş etmeye geliyoruz:

* D1 ve D2'nin her ikisi de kısıtlanmış bildirimlerse ve D1'in ilişkili kısıtlamaları D2'ninkileri içeriyorsa, D1 bildirimi en azından D2 bildirimi kadar sınırlıdır; veya * D2 ile ilişkili herhangi bir kısıtlama yoktur.

Çünkü signed_integralsubsumes integral, <signed_integral> wrapperolarak "en azından kısıtlanmış" <integral> wrapper. Ancak, tersinirlik, toplamın tersinir olmaması nedeniyle doğru değildir.

Bu nedenle, "daha kısıtlı" varlıklar kuralına uygun olarak:

D1, en azından D2 kadar kısıtlı olduğunda ve D2, en azından D1 kadar kısıtlı olmadığında, D1 bildirimi, başka bir D2 bildiriminden daha kısıtlıdır.

Yana <integral> wrapperolarak kısıtlı gibi en az değildir <signed_integral> wrapper, ikincisi daha eski daha kısıtlı olarak kabul edilmektedir.

Bu nedenle, ikisi de başvurabildiğinde, daha kısıtlı beyan kazanır.


Kısıtlama kurallarının, a olmayan bir ifadeyle karşılaşıldığında durduğunu unutmayın concept. Eğer bunu yaptıysanız:

template<typename T>
constexpr bool my_is_integral_v = std::is_integral_v<T>;

template<typename T>
concept my_signed_integral = my_is_integral_v<T> && std::is_signed_v<T>;

Bu durumda, my_signed_integral olmaz subsume std::integral. Her ne kadar my_is_integral_vaynı olarak tanımlansa da , std::is_integral_vbir kavram olmadığından, C ++ 'ın toplamlama kuralları aynı olduklarını belirlemek için akranlarından geçemez.

Böylece, toplama kuralları, atomik kavramlar üzerindeki işlemlerden kavramlar oluşturmanızı teşvik eder.


3

ile Partial_ordering_of_constraints

Bir kısıtlama P'nin, P'nin Q'nun P ve Q'daki atomik kısıtlamaların kimliğine kadar ima ettiği kanıtlanabilirse, kısıtlamanın Q içerdiği söylenir.

ve

Tüketim ilişkisi, aşağıdakileri belirlemek için kullanılan kısmi sınırlama sırasını tanımlar:

  • aşırı yük çözünürlüğünde şablon olmayan bir işlev için en uygun aday
  • aşırı yük kümesindeki şablon olmayan bir işlevin adresi
  • şablon argümanı için en iyi eşleşme
  • sınıf şablonu uzmanlıklarının kısmi sıralaması
  • işlev şablonlarının kısmi sıralaması

Ve konsept kavramları std::signed_integralaltüst eder std::integral<T>:

template < class T >
concept signed_integral = std::integral<T> && std::is_signed_v<T>;

Yani kodunuz std::signed_integraldaha "uzman" gibi, tamam .

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.