仅在可变参数模板中调用现有函数 class
Invoke existing functions only in variadic templated class
我有一个可变参数模板 class,我想在且仅当模板 class 具有函数时调用函数。
我目前的情况如下。
#include <cstdio>
#include <type_traits>
template <typename>
constexpr std::false_type hasFooHelper(long);
template <typename T>
constexpr auto hasFooHelper(int)
-> decltype( std::declval<T>().foo(), std::true_type{} );
template <typename T>
using has_foo = decltype( hasFooHelper<T>(0) );
class WithFoo
{
public:
void foo()
{
printf("%s\n", __func__);
}
};
template <class... T>
class myclass
{
public:
void invokeFoo()
{
if constexpr((has_foo<T>() && ...))
{
(T().foo(), ...);
}
}
};
int main()
{
myclass<WithFoo> a;
myclass<int> b;
myclass<WithFoo, int> c;
a.invokeFoo(); // Invokes WithFoo::foo()
b.invokeFoo(); // Do nothing
c.invokeFoo(); // Do nothing, how to make this invoke WithFoo::foo()?
return 0;
}
它在 myclass<WithFoo>
和 myclass<int>
上正常工作,但在 myclass<WithFoo, int>
上它什么都不做。
如何让 myclass<WithFoo, int>
也调用 WithFoo::foo()
?
这个:
if constexpr((has_foo<T>() && ...))
{
(T().foo(), ...);
}
检查 every T
是否有 foo()
,然后在 every 上调用 foo()
] T
.
您显然想要的是,对于每个 T
,有条件地调用它。您可以通过将其包装在特定类型的 lambda 中来做到这一点:
auto maybe_foo = [](auto x){
if constexpr (has_foo<decltype(x)>()) {
x.foo();
}
};
然后在每个类型上调用该 lambda:
(maybe_foo(T()), ...);
这可能是最简单的解决方案。缺点是您创建了一堆 T()
,即使您不能 .foo()
。您可以通过将 T
包装在标记类型中来解决这个问题:
template <typename T> struct type_t { using type = T; };
auto maybe_foo = [](auto x){
using T = typename decltype(x)::type;
if constexpr (has_foo<T>()) {
T().foo();
}
};
(maybe_foo(type_t<T>()), ...);
与所有这些不同,使用 C++17 中的 detection idiom 而不是推出您自己的版本。会容易很多:
template <typename T> using foo_type_t = decltype(std::declval<T>().foo());
template <typename T> using has_foo = is_detected<foo_type_t, T>;
我有一个可变参数模板 class,我想在且仅当模板 class 具有函数时调用函数。
我目前的情况如下。
#include <cstdio>
#include <type_traits>
template <typename>
constexpr std::false_type hasFooHelper(long);
template <typename T>
constexpr auto hasFooHelper(int)
-> decltype( std::declval<T>().foo(), std::true_type{} );
template <typename T>
using has_foo = decltype( hasFooHelper<T>(0) );
class WithFoo
{
public:
void foo()
{
printf("%s\n", __func__);
}
};
template <class... T>
class myclass
{
public:
void invokeFoo()
{
if constexpr((has_foo<T>() && ...))
{
(T().foo(), ...);
}
}
};
int main()
{
myclass<WithFoo> a;
myclass<int> b;
myclass<WithFoo, int> c;
a.invokeFoo(); // Invokes WithFoo::foo()
b.invokeFoo(); // Do nothing
c.invokeFoo(); // Do nothing, how to make this invoke WithFoo::foo()?
return 0;
}
它在 myclass<WithFoo>
和 myclass<int>
上正常工作,但在 myclass<WithFoo, int>
上它什么都不做。
如何让 myclass<WithFoo, int>
也调用 WithFoo::foo()
?
这个:
if constexpr((has_foo<T>() && ...))
{
(T().foo(), ...);
}
检查 every T
是否有 foo()
,然后在 every 上调用 foo()
] T
.
您显然想要的是,对于每个 T
,有条件地调用它。您可以通过将其包装在特定类型的 lambda 中来做到这一点:
auto maybe_foo = [](auto x){
if constexpr (has_foo<decltype(x)>()) {
x.foo();
}
};
然后在每个类型上调用该 lambda:
(maybe_foo(T()), ...);
这可能是最简单的解决方案。缺点是您创建了一堆 T()
,即使您不能 .foo()
。您可以通过将 T
包装在标记类型中来解决这个问题:
template <typename T> struct type_t { using type = T; };
auto maybe_foo = [](auto x){
using T = typename decltype(x)::type;
if constexpr (has_foo<T>()) {
T().foo();
}
};
(maybe_foo(type_t<T>()), ...);
与所有这些不同,使用 C++17 中的 detection idiom 而不是推出您自己的版本。会容易很多:
template <typename T> using foo_type_t = decltype(std::declval<T>().foo());
template <typename T> using has_foo = is_detected<foo_type_t, T>;