Вызывать существующие функции только в шаблонном классе variadi c - PullRequest
1 голос
/ 31 января 2020

У меня есть шаблонный класс variadi c, и я хочу вызывать функцию тогда и только тогда, когда функция класса шаблона имеет функцию.

То, что я имею до сих пор, это как показано ниже.

#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()?

1 Ответ

1 голос
/ 31 января 2020

This:

if constexpr((has_foo<T>() && ...))
{
    (T().foo(), ...);
}

проверяет, есть ли каждые T foo(), а затем вызывает foo() на каждые T.

То, что вы, очевидно, хотите, для каждого T, условно вызвать его. Вы можете сделать это, обернув это в лямбду для указанного c типа:

auto maybe_foo = [](auto x){
    if constexpr (has_foo<decltype(x)>()) {
        x.foo();
    }
};

И затем вызывать эту лямбду для каждого типа:

(maybe_foo(T()), ...);

Это самое простое решение, наверное. Недостатком является то, что вы создаете кучу T() с, даже если вы не можете .foo() их. Вы можете обойти это, заключив T s в тип тега:

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 вместо того, чтобы использовать свою собственную версию. Это будет намного проще:

template <typename T> using foo_type_t = decltype(std::declval<T>().foo());
template <typename T> using has_foo = is_detected<foo_type_t, T>;
...