Placeholders - Die Zweite

Inhaltsverzeichnis[Anzeigen]

Die Vereinheitlichung von Templates, Concepts und Platzhaltern geht weiter. Dieses Mal werde ich einen genaueren Blick auf eingeschränkte (Concepts) und uneingeschränkte (auto) Platzhalter in Zusammenhang mit Templates werfen.

Aber zuerst, eine kleine Erinnerungsstütze für dich (und mich). Ich behauptete in meinem letzten Artikel Concepts  - Placeholders: Eingeschränkte Platzhalter (Concepte) lassen sich überall da verwenden, an den uneingeschränkte Platzhalter (auto) zum Einsatz kommen können. Ich verwendete in dem letzten Artikel das Concept Integral für den Algorithmus gcd um ihn typsicherer zu machen. Weiter geht´s .

Syntactic sugar und mehr

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// conceptsIntegralVariations.cpp

#include <type_traits>
#include <iostream>

template<typename T>
concept bool Integral(){
  return std::is_integral<T>::value;
}

template<typename T>
requires Integral<T>()
T gcd(T a, T b){
  if( b == 0 ){ return a; }
  else{
    return gcd(b, a % b);
  }
}

template<Integral T>
T gcd1(T a, T b){
  if( b == 0 ){ return a; }
  else{
    return gcd(b, a % b);
  }
}

Integral gcd2(Integral a, Integral b){
  if( b == 0 ){ return a; }
  else{
    return gcd(b, a % b);
  }
}

auto gcd3(auto a, auto b){
  if( b == 0 ){ return a; }
  else{
    return gcd(b, a % b);
  }
}

int main(){

  std::cout << std::endl;

  std::cout << "gcd(100, 10)= "  <<  gcd(100, 10)  << std::endl;
  std::cout << "gcd1(100, 10)= " <<  gcd1(100, 10)  << std::endl;
  std::cout << "gcd2(100, 10)= " <<  gcd2(100, 10)  << std::endl;
  std::cout << "gcd3(100, 10)= " <<  gcd3(100, 10)  << std::endl;

  std::cout << std::endl;

}

Verstehe mich nicht falsch. Ich bin ein großer Fan von syntactic sugar. Dank syntactic sugar wird ein Feature einfacher anwendbar und du kannst daher ausdrucksstärkeren und weniger fehleranfälligen Code schreiben. Ist C++ eigentlich nicht nur syntactic sugar für C und Assembler?

In den Zeilen 7 bis 9 definiere ich das Concept Integral, das in den Zeilen 11 bis 18 zum Einsatz kommt. Nun wird die Syntax süßer. Anstatt das Concept in der Requires Clause (Zeile 12) zu verwenden, wende ich es an Stelle des Schlüsselworts typename oder class für den Typ-Parameter (Zeile 20) an. Es wird noch süßer. Ich verwende das Concept Integral in Zeile 28 als ein Funktions-Parameter. Klar, durch das Concept wird gcd2 zu einem Funktions-Template. Weiter geht es mit der Vereinheitlichung. Falls ich den eingeschränkten Platzhalter (Integral) in Zeile 28 mit einem uneingeschränkten Platzhalter (auto) in Zeile 35 ersetze, bekomme ich eine uneingeschränktes Funktions--Template. Sorry für den neuen Begriff uneingeschränktes Funktions-Template. Ich meine damit, dass gcd3 ein Template ist, das Werte beliebigen Typs annehmen kann.

Um diesen Sätze abzuschließen, kommt hier die Ausgabe des Programms.

 conceptsIntegralVariations

Ich habe diesen Abschnitt "Syntactic sugar und mehr" genannt. Aber warum mehr? Hier kommt die Auflösung.

Syntactic sugar und mehr

Das Funktions-Template gcd3 is mächtiger als die Funktions-Templates gcd, gcd1 oder gcd2. gcd3 besitzt zwei Typ-Parameter, die verschieden sein dürfen. Die selbe Beobachtung gilt nicht für das Funktion-Template gcd2. a und b müssen vom selben Typ sein. Die Anwendung des Funktions-Templates twoTypes in Zeile 39 sollte meinen Punkt klar machen.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// placeholders.cpp

#include <iostream>
#include <typeinfo>

template<typename T>
concept bool Integral(){
  return std::is_integral<T>::value;
}

void overload(auto t){
  std::cout << "auto : " << t << std::endl;
}

void overload(Integral t){
  std::cout << "Integral : " << t << std::endl;
}

void overload(long t){
  std::cout << "long : " << t << std::endl;
}
  
void twoTypes(auto a, auto b){
  std::cout << typeid(a).name() << std::endl;
  std::cout << typeid(b).name() << std::endl;
}


int main(){
  
  std::cout << std::endl;
  
  overload(3.14);
  overload(2010);
  overload(2020l);
  
  std::cout << std::endl;
  
  twoTypes(2010, 3.14); 
  
  std::cout << std::endl;

}

Mit ein wenig Hilfe von Run Time Type Information (RTTI) in Zeile 24 und 25, erhalte ich die String-Repräsentation beider Typen. Wie erwartet, sind die Typen int und double.

templateIntroductionError

Aber das kleine Programm zeigt mehr. Das Zusammenspiel zwischen dem uneingeschränkten Funktions-Template (Zeile 11), dem eingeschränkten Funktions-Template (Zeile 15) und der Funktion (Zeile 19) ist vollkommen intuitiv. Das ist genau das, was ich erwartet habe.

Falls du die klassische Art und Weise Templates zu deklarieren nicht magst, jetzt gibt es einen neuen Weg.

Template introduction

Anstelle dein eingeschränktes Template in der Form template<Integral T>zu deklarieren, kannst du einfach  Integral{T} schreiben. Nochmals, ich sprach von einem eingeschränkten Template. Du musst ein Concept anwenden.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// templateIntroduction.cpp

#include <type_traits>
#include <iostream>

template<typename T>
concept bool Integral(){
  return std::is_integral<T>::value;
}

Integral{T}
Integral gcd(T a, T b){
  if( b == 0 ){ return a; }
  else{
    return gcd(b, a % b);
  }
}

Integral{T} 
class ConstrainedClass{};

/*

auto{T}
auto gcd(T a, T b){
  if( b == 0 ){ return a; }
  else{
    return gcd(b, a % b);
  }
}

auto{T} 
class ConstrainedClass{};

*/


int main(){
  
  std::cout << std::endl;
  
  auto res= gcd(100, 10); 

  ConstrainedClass<int> constrainedClass;
  ConstrainedClass<double> constrainedClass1;
  
  std::cout << std::endl;

}

Ich wendete die neue Syntax für das Funktions-Template gcd in Zeile 11 und das Klassen-Template ConstrainedClass in Zeile 19 an. Das Concept schlägt zu, falls ich versuche die ConstrainedClass für double (Zeile 45) zu instanziieren.

templateIntroductionError

Um ehrlich zu sein. Ich finde es nicht intuitive, dass ich nicht einfach Integral durch auch auto so wie in den Zeilen 24 bis 33 ersetzen kann. Bis zu diesem Zeitpunkt in meinem Artikel konnte ich immer eingeschränkte Platzhalter für uneingeschränkte Platzhalter und anders herum verwenden. Das ist der rote Faden, den ich suche.

Aber klar, ich kann diese Einschränkung überwinden, indem ich ein Concept verwende, dass immer zu true evaluiert.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// templateIntroductionGeneric.cpp

#include <iostream>
#include <string>
#include <typeinfo>
#include <utility>


template<typename T>
concept bool Generic(){
  return true;
}

Generic{T}
Generic gcd(T a, T b){
  if( b == 0 ){ return a; }
  else{
    return gcd(b, a % b);
  }
}

Generic{T} 
class ConstrainedClass{
public:
  ConstrainedClass(){
    std::cout << typeid(decltype(std::declval<T>())).name() << std::endl;
  }
};


int main(){
  
  std::cout << std::endl;
  
  std::cout << "gcd(100, 10): " << gcd(100, 10) << std::endl;
  
  std::cout << std::endl;
 
  ConstrainedClass<int> genericClassInt;
  ConstrainedClass<std::string> genericClassString;
  ConstrainedClass<double> genericClassDouble;
  
  std::cout << std::endl;

}

Generic in Zeile 9 bis 12 ist mein Concept, das für alle Typen true zurückgibt. Nun kann ich die Syntax wieder vereinheitlichen und ein uneingeschränktes Funktions-Template (Zeile 14 bis 20) und ein uneingeschränktes Klassen-Template (Zeile 22 bis 28) definieren. Ich habe mich für den Ausdruck typeid(decltype(std::declval<T>())).name() in Zeile 26 zu entschuldigen, der selbst dann funktioniert, falls T kein Default-Konstruktor besitzt. Der Ausdruck gibt die String-Repräsentierung des Typ-Parameters T zurück. Zum Abschluss, die Ausgabe des Programms.

templateIntroductionGeneric

Wie geht's weiter?

Ich verwende bisher immer das Concept Integral in meinen Beispielen. Gelangweilt? Mein nächster Artikel geht auf die Definition von Concepts ein.

 

 

 

 

Hier geht es zum Source Code aller Artikel. Source Code Repository

 

title page smalltitle page small Go to Leanpub/cpplibrary "What every professional C++ programmer should know about the C++ standard library".   Hole dir dein E-Book. Unterstütze meinen Blog.

Kommentar schreiben


Modernes C++

Abonniere den Newsletter

Inklusive zwei Artikel meines Buchs
Introduction und Multithreading

Beiträge-Archiv

Sourcecode

Neuste Kommentare