Использование черт типа в пакетах параметров шаблона? - PullRequest
1 голос
/ 12 января 2020

В приведенном ниже примере кода я пытаюсь проверить, являются ли аргументы функции указателями или нет с 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;
}

Полагаю, если параметры не являются ни указателями, ни указателями, то у нас возникает дополнительная проблема, но давайте просто предположим, что все аргументы являются либо указателями, либо нет.

тогда как насчет того, чтобы аргументы были смесью указателей и не указателей в любом случае?

Ответы [ 3 ]

6 голосов
/ 12 января 2020

Вы можете использовать кратное выражение :

#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[] s. В моем исходном примере test(new int, new char, new int, "hello"); даст вывод yes, а с Ts&&... ts - no.

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

Эту проблему можно упростить (и заставить программу работать), переместив определение, является ли параметр указателем, в переменную c шаблонную функцию 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

https://godbolt.org/z/y57-TA

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

Для любого, кто использует 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
...