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 a
Werke (und die tuple
Spezialisierung ist tatsächlich gewählt).
Die Instanziierung für b
schlä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_if
nicht nützlich sind, aber dies ist hier nur ein Beispiel.
Eine Möglichkeit wäre, die B
Lö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_same
erscheint hier nicht sinnvoll.
clang++ gibt mir in all diesen Fällen die gleichen Fehler.
Lösung des Problems
Für die Spezialisierung B
müssen Sie sicherstellen, dass die Bedingungen zu std::enable_if
orthogonal sind. Wenn Sie in Ihrer Version v=12
sowohl Bedingungen v!=11
als auch v==12
Ertrag 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