仅在可变参数模板中调用现有函数 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>;