在模板参数包上使用类型特征?
Using type traits on template parameter packs?
在下面的示例代码中,我尝试使用 std::is_pointer
检查函数参数是否为指针
如果只有一个参数可以正常工作,但是如何让它与更多参数一起工作,比如在参数包中?
#include <type_traits>
#include <iostream>
class Test
{
public:
template<typename... Params>
void f(Params... params);
template<typename T, typename... Params>
auto sum(T arg, Params... params)
{
return arg + sum(params...);
}
template<typename T>
auto sum(T arg)
{
return arg;
}
int member = 1;
};
template<typename... Params>
void Test::f(Params... params)
{
// ERROR: too many template arguments for std::is_pointer
if constexpr (std::is_pointer_v<Params...>)
member += sum(*params...);
else
member += sum(params...);
std::cout << member;
}
int main()
{
Test ts;
// both fail
ts.f(1, 2);
ts.f(&ts.member, &ts.member);
// is that even possible?
ts.f(&ts.member, 2);
return 0;
}
我想如果参数不是全是指针或全不是指针,那么我们还有其他问题,但让我们假设所有参数要么是指针,要么不是。
那么如果参数是指针和非指针的混合呢?
您可以使用 fold expression:
#include <iostream>
#include <type_traits>
template <typename... Ts>
void test(Ts... ts) {
if constexpr ((std::is_pointer_v<Ts> && ...)) {
std::cout << "yes\n";
} else {
std::cout << "no\n";
}
}
int main() {
test(new int, new char, new int);
test(new int, new char, new int, 2);
}
程序的输出:
yes
no
不过请注意您的函数模板签名 - 我建议使用 Ts&&... ts
而不是 Ts... ts
,因为 const char[]
的处理方式。在我的原始示例中,test(new int, new char, new int, "hello");
将产生 yes
输出,但对于 Ts&&... ts
,它将产生 no
.
对于任何使用 C++11/14 的人,您可以使用以下解决方案。
// helper struct that checks the list
template<int N, typename... Args>
struct is_all_pointer_helper;
// N > 1, checks the head and recursively checks the tail
template<int N, typename Arg, typename... Args>
struct is_all_pointer_helper<N, Arg, Args...> {
static constexpr bool value =
std::is_pointer<Arg>::value &&
is_all_pointer_helper<N-1, Args...>::value;
};
// N == 1, checks the head (end of recursion)
template<typename Arg, typename... Args>
struct is_all_pointer_helper<1, Arg, Args...> {
static constexpr bool value = std::is_pointer<Arg>::value;
};
// N == 0, define your result for the empty list
template<> struct is_all_pointer_helper<0> {
static constexpr bool value = false;
};
// user interface
template<typename... Args>
struct is_all_pointer : is_all_pointer_helper<sizeof...(Args), Args...> {};
// C++14 only
template<typename... Args>
constexpr bool is_all_pointer_v = is_all_pointer<Args...>::value;
class Foo {};
int main()
{
cout << std::boolalpha << is_all_pointer<int*, char*, Foo*>::value << endl;
cout << std::boolalpha << is_all_pointer_v<int*, char, Foo*> << endl; //C++14
cout << std::boolalpha << is_all_pointer<>::value << endl;
}
输出:
true
false
false
通过将参数是否为指针的检测移动到可变参数模板函数中,可以简化问题(并使程序运行)sum
。
示例:
#include <type_traits>
#include <iostream>
class Test
{
public:
template<typename... Params>
void f(Params... params)
{
member += sum(params...);
std::cout << member << '\n';
}
template<typename... Params>
auto sum(Params... params)
{
auto contents = [](auto param)
{
using ParamType = std::decay_t<decltype(param)>;
if constexpr (std::is_pointer_v<ParamType>)
return *param;
else
return param;
};
return (contents(params) + ...);
}
int member = 1;
};
int main()
{
Test ts;
// both fail
ts.f(1, 2);
ts.f(&ts.member, &ts.member);
// is that even possible?
ts.f(&ts.member, 2);
return 0;
}
预期输出:
4
12
26
在下面的示例代码中,我尝试使用 std::is_pointer
如果只有一个参数可以正常工作,但是如何让它与更多参数一起工作,比如在参数包中?
#include <type_traits>
#include <iostream>
class Test
{
public:
template<typename... Params>
void f(Params... params);
template<typename T, typename... Params>
auto sum(T arg, Params... params)
{
return arg + sum(params...);
}
template<typename T>
auto sum(T arg)
{
return arg;
}
int member = 1;
};
template<typename... Params>
void Test::f(Params... params)
{
// ERROR: too many template arguments for std::is_pointer
if constexpr (std::is_pointer_v<Params...>)
member += sum(*params...);
else
member += sum(params...);
std::cout << member;
}
int main()
{
Test ts;
// both fail
ts.f(1, 2);
ts.f(&ts.member, &ts.member);
// is that even possible?
ts.f(&ts.member, 2);
return 0;
}
我想如果参数不是全是指针或全不是指针,那么我们还有其他问题,但让我们假设所有参数要么是指针,要么不是。
那么如果参数是指针和非指针的混合呢?
您可以使用 fold expression:
#include <iostream>
#include <type_traits>
template <typename... Ts>
void test(Ts... ts) {
if constexpr ((std::is_pointer_v<Ts> && ...)) {
std::cout << "yes\n";
} else {
std::cout << "no\n";
}
}
int main() {
test(new int, new char, new int);
test(new int, new char, new int, 2);
}
程序的输出:
yes no
不过请注意您的函数模板签名 - 我建议使用 Ts&&... ts
而不是 Ts... ts
,因为 const char[]
的处理方式。在我的原始示例中,test(new int, new char, new int, "hello");
将产生 yes
输出,但对于 Ts&&... ts
,它将产生 no
.
对于任何使用 C++11/14 的人,您可以使用以下解决方案。
// helper struct that checks the list
template<int N, typename... Args>
struct is_all_pointer_helper;
// N > 1, checks the head and recursively checks the tail
template<int N, typename Arg, typename... Args>
struct is_all_pointer_helper<N, Arg, Args...> {
static constexpr bool value =
std::is_pointer<Arg>::value &&
is_all_pointer_helper<N-1, Args...>::value;
};
// N == 1, checks the head (end of recursion)
template<typename Arg, typename... Args>
struct is_all_pointer_helper<1, Arg, Args...> {
static constexpr bool value = std::is_pointer<Arg>::value;
};
// N == 0, define your result for the empty list
template<> struct is_all_pointer_helper<0> {
static constexpr bool value = false;
};
// user interface
template<typename... Args>
struct is_all_pointer : is_all_pointer_helper<sizeof...(Args), Args...> {};
// C++14 only
template<typename... Args>
constexpr bool is_all_pointer_v = is_all_pointer<Args...>::value;
class Foo {};
int main()
{
cout << std::boolalpha << is_all_pointer<int*, char*, Foo*>::value << endl;
cout << std::boolalpha << is_all_pointer_v<int*, char, Foo*> << endl; //C++14
cout << std::boolalpha << is_all_pointer<>::value << endl;
}
输出:
true false false
通过将参数是否为指针的检测移动到可变参数模板函数中,可以简化问题(并使程序运行)sum
。
示例:
#include <type_traits>
#include <iostream>
class Test
{
public:
template<typename... Params>
void f(Params... params)
{
member += sum(params...);
std::cout << member << '\n';
}
template<typename... Params>
auto sum(Params... params)
{
auto contents = [](auto param)
{
using ParamType = std::decay_t<decltype(param)>;
if constexpr (std::is_pointer_v<ParamType>)
return *param;
else
return param;
};
return (contents(params) + ...);
}
int member = 1;
};
int main()
{
Test ts;
// both fail
ts.f(1, 2);
ts.f(&ts.member, &ts.member);
// is that even possible?
ts.f(&ts.member, 2);
return 0;
}
预期输出:
4
12
26