可变参数模板中的模板专业化和选择 class
Template specialization and selection in variadic template class
我有一个可变参数模板 class,其中包含一些模板专业化。对于某些专业化,我想有条件地启用它们(使用 std::enable_if
,因此我添加了一个额外的未使用的模板参数)。
这是一个代码示例:
#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;
}
a
的实例化有效(确实选择了 tuple
专业化)。
b
的实例化因模板实例化不明确而失败。使用 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;
| ^
对于C,读取模板本身时出现错误(无需实例化):
$ 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’
可以做什么?
注意:我知道std::enable_if
中的条件没有用,但这只是一个例子。
一种方法是使用 B
解决方案,但更改 std::enable_if
条件以禁用第二个模板特化,但如何说“不是任何类型的元组”? std::is_same
在这里似乎没有用。
clang++ 在所有这些情况下都会给我同样的错误。
对于专业化 B
,您需要确保 std::enable_if
的条件是正交的。在您的版本中,如果您提供 v=12
两个条件 v!=11
和 v==12
会产生 true
,这意味着两个版本都已启用。这就是为什么您会收到模棱两可的实例化错误的原因。以下编译正常(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();
}
更新:正如评论中所问,检查某个模板参数是否是任何类型的元组的方法可以如下完成(也可以比较 this post). Is this closer what you want to achieve? (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
}
我有一个可变参数模板 class,其中包含一些模板专业化。对于某些专业化,我想有条件地启用它们(使用 std::enable_if
,因此我添加了一个额外的未使用的模板参数)。
这是一个代码示例:
#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;
}
a
的实例化有效(确实选择了 tuple
专业化)。
b
的实例化因模板实例化不明确而失败。使用 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;
| ^
对于C,读取模板本身时出现错误(无需实例化):
$ 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’
可以做什么?
注意:我知道std::enable_if
中的条件没有用,但这只是一个例子。
一种方法是使用 B
解决方案,但更改 std::enable_if
条件以禁用第二个模板特化,但如何说“不是任何类型的元组”? std::is_same
在这里似乎没有用。
clang++ 在所有这些情况下都会给我同样的错误。
对于专业化 B
,您需要确保 std::enable_if
的条件是正交的。在您的版本中,如果您提供 v=12
两个条件 v!=11
和 v==12
会产生 true
,这意味着两个版本都已启用。这就是为什么您会收到模棱两可的实例化错误的原因。以下编译正常(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();
}
更新:正如评论中所问,检查某个模板参数是否是任何类型的元组的方法可以如下完成(也可以比较 this post). Is this closer what you want to achieve? (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
}