GOOGLE ADS

Dienstag, 12. April 2022

Template-Spezialisierung und -Auswahl in der variadischen Template-Klasse

Ich habe eine Variadic-Template-Klasse mit einigen Template-Spezialisierungen. Ich möchte sie für einige Spezialisierungen bedingt aktivieren (mit std::enable_if, also habe ich einen zusätzlichen nicht verwendeten Vorlagenparameter hinzugefügt).

Hier ist ein Codebeispiel:

#include <tuple>
#include <iostream>
template<int v,typename, typename...>
struct A;
template<int v, typename...Ts, typename...Us>
struct A<v,void,std::tuple<Ts...>, Us...> {
void print() {
std::cout << "A: Tuple selected" << std::endl;
}
};
template<int v, typename T, typename...Us>
struct A<v,void,T, Us...> {
void print() {
std::cout << "A: one element selected" << std::endl;
}
};
template<int v,typename, typename...>
struct B;
template<int v, typename...Ts, typename...Us>
struct B<v,std::enable_if_t<v!=11>,std::tuple<Ts...>, Us...> {
void print() {
std::cout << "B: Tuple selected" << std::endl;
}
};
template<int v, typename T, typename...Us>
struct B<v,std::enable_if_t<v==12>,T, Us...> {
void print() {
std::cout << "B: one element selected" << std::endl;
}
};
template<int v, typename...>
struct C;
template<int v, typename...Ts, typename...Us>
struct C<v,std::enable_if_t<v!=11,std::tuple<Ts...>>, Us...> {
void print() {
std::cout << "C: Tuple selected" << std::endl;
}
};
template<int v, typename T, typename...Us>
struct C<v, T, Us...> {
void print() {
std::cout << "C: one element selected" << std::endl;
}
};
int main()
{
struct A<12,void,std::tuple<int>> a;
a.print();
struct B<12,void,std::tuple<int>> b;
b.print();
struct C<12,std::tuple<int>> c;
c.print();
return 0;
}

Instanziierung für aWerke (und die tupleSpezialisierung ist tatsächlich gewählt).

Die Instanziierung für bschlägt aufgrund einer mehrdeutigen Template-Instatiation fehl. Mit gcc:

$ g++ -std=gnu++17 -Wall -Wextra toto5.cpp
toto5.cpp: In function 'int main()':
toto5.cpp:61:38: error: ambiguous template instantiation for 'struct B<12, void, std::tuple<int> >'
61 | struct B<12,void,std::tuple<int>> b;
| ^
toto5.cpp:25:8: note: candidates are: 'template<int v, class... Ts, class... Us> struct B<v, typename std::enable_if<(v!= 11), void>::type, std::tuple<_UTypes...>, Us...> [with int v = 12; Ts = {int}; Us = {}]'
25 | struct B<v,std::enable_if_t<v!=11>,std::tuple<Ts...>, Us...> {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
toto5.cpp:32:8: note: 'template<int v, class T, class... Us> struct B<v, typename std::enable_if<(v == 12), void>::type, T, Us...> [with int v = 12; T = std::tuple<int>; Us = {}]'
32 | struct B<v,std::enable_if_t<v==12>,T, Us...> {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
toto5.cpp:61:38: error: aggregate 'B<12, void, std::tuple<int> > b' has incomplete type and cannot be defined
61 | struct B<12,void,std::tuple<int>> b;
| ^

Für C tritt der Fehler beim Lesen der Vorlage selbst auf (keine Instanziierung erforderlich):

$ g++ -std=gnu++17 -Wall -Wextra toto5.cpp
toto5.cpp:42:8: error: template parameters not deducible in partial specialization:
42 | struct C<v,std::enable_if_t<v!=11,std::tuple<Ts...>>, Us...> {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
toto5.cpp:42:8: note: 'Ts'

Was kann getan werden?

Hinweis: Ich weiß, dass die Bedingungen in std::enable_ifnicht nützlich sind, aber dies ist hier nur ein Beispiel.

Eine Möglichkeit wäre, die BLösung zu verwenden, aber die Bedingung zu ändern std::enable_if, um die zweite Vorlagenspezialisierung zu deaktivieren, aber wie sagt man "kein Tupel irgendeiner Art"? std::is_sameerscheint hier nicht sinnvoll.

clang++ gibt mir in all diesen Fällen die gleichen Fehler.


Lösung des Problems

Für die Spezialisierung Bmüssen Sie sicherstellen, dass die Bedingungen zu std::enable_iforthogonal sind. Wenn Sie in Ihrer Version v=12sowohl Bedingungen v!=11als auch v==12Ertrag angeben true, bedeutet dies, dass beide Versionen aktiviert sind. Aus diesem Grund erhalten Sie den mehrdeutigen Instanziierungsfehler. Folgendes lässt sich gut kompilieren ( https://godbolt.org/z/csaTWMd9v ):

#include <tuple>
#include <iostream>
template<int v,typename, typename...>
struct B;
template<int v, typename...Ts, typename...Us>
struct B<v,std::enable_if_t<v!=11>,std::tuple<Ts...>, Us...> {
void print() {
std::cout << "B: Tuple selected" << std::endl;
}
};
template<int v, typename T, typename...Us>
struct B<v,std::enable_if_t<v==11>,T, Us...> {
void print() {
std::cout << "B: one element selected" << std::endl;
}
};
int main()
{
struct B<12,void,std::tuple<int>> b;
b.print();
}

Update: Wie in den Kommentaren gefragt, kann wie folgt überprüft werden, ob ein bestimmter Vorlagenparameter ein Tupel irgendeiner Art ist (siehe auch z. B. diesen Beitrag ). Ist das näher, was Sie erreichen wollen? ( https://godbolt.org/z/dq85fM7KK )

#include <tuple>
#include <iostream>
template <typename>
struct is_tuple: std::false_type
{ };
template <typename... T>
struct is_tuple<std::tuple<T...>>: std::true_type
{ };
template<int v,typename, typename...>
struct B;
template<int v, typename...Ts, typename...Us>
struct B<v, std::enable_if_t<v!=11>, std::tuple<Ts...>, Us...> {
void print() {
std::cout << "B: Tuple selected" << std::endl;
}
};
template<int v, typename T, typename...Us>
struct B<v,std::enable_if_t<v==12 &&!is_tuple<T>::value>, T, Us...> {
void print() {
std::cout << "B: one element selected" << std::endl;
}
};
int main()
{
B<12, void, std::tuple<int>>{}.print(); // Tuple selected
B<12, void, int>{}.print(); // one element selected
}

Keine Kommentare:

Kommentar veröffentlichen

Warum werden SCHED_FIFO-Threads derselben physischen CPU zugewiesen, obwohl CPUs im Leerlauf verfügbar sind?

Lösung des Problems Wenn ich das richtig verstehe, versuchen Sie, SCHED_FIFO mit aktiviertem Hyperthreading ("HT") zu verwenden, ...