可变参数模板方法
Variadic template method
我会创建一个 class 方法,它接受可变参数(在模板中),然后使用它们来获取(例如)传递的参数的名称。
我尝试实现(但它不起作用):
#include <iostream>
struct St1 {};
struct St2 {};
struct St3 {};
class Test {
public:
template<typename T>
void method()
{
std::cout << typeid(T).name() << std::endl; // for example
}
template<typename T, typename ...Targs>
void method()
{
method<T>();
method<Targs...>();
}
};
int main(void) {
Test a;
a.method<St1, St2, St3>();
a.method<St1>();
}
当我想编译时出现这些错误,'ambiguous':
testSpeed.cpp:29:19: error: call of overloaded ‘method()’ is ambiguous
a.method<St1>();
^
testSpeed.cpp:12:10: note: candidate: void Test::method() [with T = St1]
void method()
^~~~~~
testSpeed.cpp:18:10: note: candidate: void Test::method() [with T = St1; Targs = {}]
void method()
^~~~~~
testSpeed.cpp: In instantiation of ‘void Test::method() [with T = St1; Targs = {St2, St3}]’:
testSpeed.cpp:28:29: required from here
testSpeed.cpp:20:18: error: call of overloaded ‘method()’ is ambiguous
method<T>();
~~~~~~~~~^~
testSpeed.cpp:12:10: note: candidate: void Test::method() [with T = St1]
void method()
^~~~~~
testSpeed.cpp:18:10: note: candidate: void Test::method() [with T = St1; Targs = {}]
void method()
当参数包为空时,您可以使用 SFINAE 禁用可变版本:
template<typename T, typename ...Targs,
std::enable_if_t<sizeof...(Targs) != 0>* = nullptr>
void method()
{
method<T>();
method<Targs...>();
}
在 C++17 中,这非常简单:
class Test {
public:
template<typename T, typename... Ts>
void method() {
std::cout << typeid(T).name() << std::endl;
if constexpr (sizeof...(Ts) > 0)
method<Ts...>();
}
};
在 C++11 中你可以这样做:
class Test {
template<class> struct Tag {};
template<typename T>
void method_impl(Tag<T>) {
std::cout << typeid(T).name() << std::endl;
}
template<typename T, typename... Ts>
void method_impl(Tag<T> tag, Tag<Ts>... tags) {
method_impl(tag);
method_impl(tags...);
}
public:
template<typename... Ts>
void method() {
method_impl(Tag<Ts>{}...);
}
};
稍作修改即可自动处理空包:
class Test {
template<class> struct Tag {};
void method_impl() { }
template<typename T, typename... Ts>
void method_impl(Tag<T>, Tag<Ts>... tags) {
std::cout << typeid(T).name() << std::endl;
method_impl(tags...);
}
public:
template<typename... Ts>
void method() {
method_impl(Tag<Ts>{}...);
}
};
函数参数允许select重载,而不是模板参数。
一种解决方案是将模板参数转换为参数:
template <typename ... Ts> struct Tag{};
class Test {
public:
template<typename T>
void method(Tag<T>)
{
std::cout << typeid(T).name() << std::endl; // for example
}
template<typename T, typename ...Ts>
void method(Tag<T, Ts...>)
{
method(Tag<T>{});
method(Tag<Ts...>{});
}
};
int main() {
Test a;
a.method(Tag<St1, St2, St3>{});
a.method(Tag<St1>{});
}
或
template <typename T> struct Tag{};
class Test {
public:
template<typename T>
void method(Tag<T>)
{
std::cout << typeid(T).name() << std::endl; // for example
}
template<typename T, typename ...Ts>
void method(Tag<T>, Tag<Ts>...)
{
method(Tag<T>{});
method(Tag<Ts>{}...);
}
};
int main() {
Test a;
a.method(Tag<St1>{}, Tag<St2>{}, Tag<St3>{});
a.method(Tag<St1>{});
}
但您可以摆脱递归调用(和重载):
C++17 中的折叠表达式:
template<typename ... Ts>
void method()
{
((std::cout << typeid(Ts).name() << std::endl), ...);
}
在 C++11/C++14 中,它更冗长,更不清晰:
template<typename ... Ts>
void method()
{
std::initializer_list<int>{0, ((std::cout << typeid(Ts).name() << std::endl), 0)...};
}
我会创建一个 class 方法,它接受可变参数(在模板中),然后使用它们来获取(例如)传递的参数的名称。
我尝试实现(但它不起作用):
#include <iostream>
struct St1 {};
struct St2 {};
struct St3 {};
class Test {
public:
template<typename T>
void method()
{
std::cout << typeid(T).name() << std::endl; // for example
}
template<typename T, typename ...Targs>
void method()
{
method<T>();
method<Targs...>();
}
};
int main(void) {
Test a;
a.method<St1, St2, St3>();
a.method<St1>();
}
当我想编译时出现这些错误,'ambiguous':
testSpeed.cpp:29:19: error: call of overloaded ‘method()’ is ambiguous
a.method<St1>();
^
testSpeed.cpp:12:10: note: candidate: void Test::method() [with T = St1]
void method()
^~~~~~
testSpeed.cpp:18:10: note: candidate: void Test::method() [with T = St1; Targs = {}]
void method()
^~~~~~
testSpeed.cpp: In instantiation of ‘void Test::method() [with T = St1; Targs = {St2, St3}]’:
testSpeed.cpp:28:29: required from here
testSpeed.cpp:20:18: error: call of overloaded ‘method()’ is ambiguous
method<T>();
~~~~~~~~~^~
testSpeed.cpp:12:10: note: candidate: void Test::method() [with T = St1]
void method()
^~~~~~
testSpeed.cpp:18:10: note: candidate: void Test::method() [with T = St1; Targs = {}]
void method()
当参数包为空时,您可以使用 SFINAE 禁用可变版本:
template<typename T, typename ...Targs,
std::enable_if_t<sizeof...(Targs) != 0>* = nullptr>
void method()
{
method<T>();
method<Targs...>();
}
在 C++17 中,这非常简单:
class Test {
public:
template<typename T, typename... Ts>
void method() {
std::cout << typeid(T).name() << std::endl;
if constexpr (sizeof...(Ts) > 0)
method<Ts...>();
}
};
在 C++11 中你可以这样做:
class Test {
template<class> struct Tag {};
template<typename T>
void method_impl(Tag<T>) {
std::cout << typeid(T).name() << std::endl;
}
template<typename T, typename... Ts>
void method_impl(Tag<T> tag, Tag<Ts>... tags) {
method_impl(tag);
method_impl(tags...);
}
public:
template<typename... Ts>
void method() {
method_impl(Tag<Ts>{}...);
}
};
稍作修改即可自动处理空包:
class Test {
template<class> struct Tag {};
void method_impl() { }
template<typename T, typename... Ts>
void method_impl(Tag<T>, Tag<Ts>... tags) {
std::cout << typeid(T).name() << std::endl;
method_impl(tags...);
}
public:
template<typename... Ts>
void method() {
method_impl(Tag<Ts>{}...);
}
};
函数参数允许select重载,而不是模板参数。
一种解决方案是将模板参数转换为参数:
template <typename ... Ts> struct Tag{};
class Test {
public:
template<typename T>
void method(Tag<T>)
{
std::cout << typeid(T).name() << std::endl; // for example
}
template<typename T, typename ...Ts>
void method(Tag<T, Ts...>)
{
method(Tag<T>{});
method(Tag<Ts...>{});
}
};
int main() {
Test a;
a.method(Tag<St1, St2, St3>{});
a.method(Tag<St1>{});
}
或
template <typename T> struct Tag{};
class Test {
public:
template<typename T>
void method(Tag<T>)
{
std::cout << typeid(T).name() << std::endl; // for example
}
template<typename T, typename ...Ts>
void method(Tag<T>, Tag<Ts>...)
{
method(Tag<T>{});
method(Tag<Ts>{}...);
}
};
int main() {
Test a;
a.method(Tag<St1>{}, Tag<St2>{}, Tag<St3>{});
a.method(Tag<St1>{});
}
但您可以摆脱递归调用(和重载):
C++17 中的折叠表达式:
template<typename ... Ts>
void method()
{
((std::cout << typeid(Ts).name() << std::endl), ...);
}
在 C++11/C++14 中,它更冗长,更不清晰:
template<typename ... Ts>
void method()
{
std::initializer_list<int>{0, ((std::cout << typeid(Ts).name() << std::endl), 0)...};
}