Захват типа аргумента функции из фактических аргументов

StackOverflow https://stackoverflow.com//questions/25034811

Вопрос

Можно ли определить тип формального аргумента, имея только имя функции и фактические аргументы?

Мне нужно что-то похожее на decltype, но оно должно возвращать тип функции вместо типа возвращаемого значения функции.

Я нашел половинчатое решение с C++11.Если функция не перегружена, вы можете использовать decltype для получения подписи, указав только имя функции и аргументы шаблона (если есть), но не сами аргументы:

template<class F>
struct my_function_traits;

// function pointer
template<class R, class Arg>
struct my_function_traits<R(*)(Arg)> : public my_function_traits<R(Arg)>
{
  typedef Arg type;
};

template<class R, class Arg1>
struct my_function_traits<R(Arg1)>
{
  typedef Arg1 type;
};

namespace itk
{
template <typename PixelType, unsigned Dimension>
class Image;
}

template <typename TPixel, unsigned VDimension>               
void func2(itk::Image<TPixel, VDimension>* image) {}


int main()
{
  typedef my_function_traits< decltype(func2<int, 2>) > Traits;

  Traits::type var = 0;

  return 0;
}

Это работает хорошо, но если я добавлю еще одну функцию, decltype не сможет определить тип:

template <typename TPixel, unsigned VDimension>               
void func2(const itk::Image<TPixel, VDimension>* image, int i) {}

В этом есть смысл, поскольку имя функции неоднозначно и обе версии имеют одинаковые аргументы шаблона.Но компилятор должен иметь возможность определить правильную функцию по статическому типу фактических аргументов.Проблема в том, что как только я их указываю, decltype возвращает тип выражения, т.е.тип возвращаемого значения функции.

  itk::Image<int, 3>* image;
  int a = 3;
  ...
  typedef my_function_traits< decltype(func2<int, 2>(image, 3)) > Traits;

Моя главная цель — проверить константность первого аргумента.

Спасибо за любую помощь!

Это было полезно?

Решение

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

template <typename T, typename ...Ts>
struct is_first_arg_const_ptr
{
    template <typename Ret>
    static std::false_type
    is_const(Ret (&f)(T*, Ts...));

    template <typename Ret>
    static std::true_type
    is_const(Ret (&f)(const T*, Ts...));

    // And for methods
    template <typename Ret, typename C>
    static std::false_type
    is_const (Ret (C::*) (T*, Ts...)) /* const volatile & && */;

    template <typename Ret, typename C>
    static std::true_type
    is_const (Ret (C::*) (const T*, Ts...)) /* const volatile & && */;

    // .. complete for all combinations of cv_qualifier and ref
};

И вот перегрузка выбрана правильно и без двусмысленности.

Живой пример.

Для макроса вы можете сделать что-то вроде:

template <typename T, typename ...Ts>
constexpr
auto make_is_first_arg_const_ptr(T&& t, Ts&&...)
-> is_first_arg_const_ptr<
    typename std::decay<decltype(*t)>::type,
    typename std::decay<Ts>::type...
>;

#define IS_FIRST_ARG_CONST_PTR(Name, ...) \
    (decltype(make_is_first_arg_const_ptr(__VA_ARGS__).is_const(Name))::value)

А затем использовать его как

static_assert(!IS_FIRST_ARG_CONST_PTR(func2, image), "");
static_assert(IS_FIRST_ARG_CONST_PTR(func2, image, a), "");
static_assert(IS_FIRST_ARG_CONST_PTR(&MyClass::func2, image, a), "");

Обратите внимание, что макрос не будет работать для некоторых прототипов func поскольку я вывожу тип из аргумента, но в этом случае вы получите ошибку компиляции (вы можете изменить std::decay более подходящими характеристиками, отвечающими вашим потребностям).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top