Как написать шаблон, который может определить тип, используя тип аргумента функции? - PullRequest
0 голосов
/ 11 января 2019

Как написать шаблон, который использует функцию в качестве параметра шаблона, и автоматически определить другое имя типа по типу аргумента этой функции?

void foo(int *p) {}

template<typename T, void (*F)(T*)>
struct bar
{
    bar(T* t)
    {
        F(t);
    }
}

int *p;
bar<int, foo> b(p); // both int and foo are required here

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

bar<foo> b(p);

Ответы [ 3 ]

0 голосов
/ 11 января 2019

В C ++ 11 классы не могут определить все типы переданной функции. Но функция может. Таким образом, эта функция может быть написана:

template<typename Ret, typename Param>
Deduced_Types<Ret, Param> deduce_type(Ret (*)(Param))
{
    return Deduced_Types<Ret, Param>();
}

Эта функция использует тип для хранения выведенных типов (она должна быть объявлена ​​перед функцией):

template<typename Ret, typename Param>
class Deduced_Types
{
public:
    typedef Ret Return_type;
    typedef Param Parameter_type;
};

Теперь, чтобы проверить это;

int f(bool)
{
    return 0;
}

int main(int argc, char* argv[])
{
    decltype( deduce_type(f) )::Return_type i = 0;

    return i;
}

Работает.

Итак, теперь Бар:

template< class F >
class Bar
{
public:
    typedef typename F::Return_type Func_Return_type;
    typedef typename F::Parameter_type Fun_Param_type;

};

который должен называться:

Bar< decltype(deduce_type(f)) > b;

(здесь вы можете использовать макрос)

Работает на gcc 4.8.1: https://godbolt.org/z/1cz2pk


Edit:

Предварительно функция C ++ 17 не может быть передана в шаблон.

Итак, вам нужно передать указатель на функцию в класс. И static_assert для проверки правильности параметров:

#include <type_traits>

struct bar
{
    template<typename F, typename T>
    bar(F f, T t)
    {
        typedef decltype(deduce_type(f)) D;
        static_assert(std::is_same<typename D::Parameter_type, T>::value,"parameter has different type function parameter");

        f(t);
    }
};

Теперь вместо передачи функции в качестве параметра шаблона указатель функции передается в качестве параметра:

void foo(int *p) {}

int *p;
bar b(foo, p); 

Единственная проблема здесь в том, что класс должен хранить этот указатель для будущего использования.

0 голосов
/ 11 января 2019

В C ++ 17 вы можете сделать

template <auto> struct bar;

template<typename T, void (*F)(T*)>
struct bar<F>
{
    bar(T* t) { F(t); }
};

с использованием:

int* p = nullptr;
bar<foo> b(p);

До C ++ 17 вы могли бы сделать:

template <typename T, T> struct bar;

template<typename T, void (*F)(T*)>
struct bar<void (*)(T*), F>
{
    bar(T* t) { F(t); }
};

При использовании:

int* p = nullptr;
bar<decltype(&foo), &foo> b(p);

MACRO можно использовать для удаления дублирования, например:

#define BAR(Func) bar<decltype(&Func), &Func>
#define TYPE_AND_VALUE(V) decltype(V), V>

чтобы разрешить:

BAR(foo) b(p);
bar<TYPE_AND_VALUE(&foo)> b(p);
0 голосов
/ 11 января 2019

Если вы можете использовать c ++ 17 с его автоматическим параметром шаблона (как @ n.m. Сказано в комментариях), вы можете использовать его как параметр шаблона, а затем тип T с чертами типа.

Во-первых, нам нужны стандартные черты типа, а также черта типа, чтобы получить аргумент унарной функции (твое foo), которую мы можем написать так:

#include <type_traits>

// Trait to get the argument type of a unary function
template<typename T>
struct unary_func_arg;

template<typename R, typename T>
struct unary_func_arg< R(*)(T) >
{
    using type = T;
};

Это приведет к ошибке, если вы поместите в него что-либо, кроме указателя функции, так как основная специализация не объявлена.

После этого мы можем наконец написать строку так:

template< auto F >
struct bar
{
    // Assert it's a function pointer
    static_assert( std::is_pointer_v<decltype(F)> );
    static_assert( std::is_function_v< std::remove_pointer_t<decltype(F)> > );

    // Get the parameter type
    using T = typename unary_func_arg< decltype(F) >::type;

    bar(T t)
    {
        F(t);
    }
};

Мы должны убедиться, что F является указателем на функцию, поэтому мы статически утверждаем это, тогда мы получаем тип T из нашей черты типа.

Теперь вы можете объявить f и b следующим образом:

int* p;
bar<foo> b(p);

РЕДАКТИРОВАТЬ: Если вам нужно, чтобы T не был указателем, поэтому вы можете написать T *, вы можете либо создать черту типа, которая удаляет 1 уровень указателя, либо изменить здесь черту типа следующим образом:

// Trait to get the argument type of a unary function
template<typename T>
struct unary_func_arg_pointer;

template<typename R, typename T>
struct unary_func_arg_pointer< R(*)(T*) >
{
    using type = T;
};

Теперь в этом примере T будет просто int

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...