Частично специализируемый шаблон класса от lambda - PullRequest
4 голосов
/ 05 апреля 2019

У меня есть тип шаблона, в котором хранится информация о функции или функции-члене (например, тип возвращаемого значения, число или параметры и т. Д.).

template<class R, class... FuncParams>
struct SFuncInfo
{
    using Signature = R(FuncParams...);
    using Ret = R;

    static constexpr size_t numParams = sizeof...(FuncParams);
};

// member
template<class T, class Ret, class... Params>
struct SFuncInfo<Ret(T::*)(Params...)> : SFuncInfo<Ret, Params...> 
{
    static constexpr bool isMemberFunction = true;
};

// function
template<class R, class... FuncParams>
struct SFuncInfo<R(FuncParams...)> : SFuncInfo<R, FuncParams...> 
{
    static constexpr bool isMemberFunction = false;
};

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

int func(const char* str) { return 1; }

struct MyType
{
    bool memFunc(int val, float fl) { return true; }
};

int main() 
{
    static_assert(!SFuncInfo<decltype(func)>::isMemberFunction, "");
    static_assert(std::is_same<SFuncInfo<decltype(func)>::Ret, int>::value, "");

    static_assert(SFuncInfo<decltype(&MyType::memFunc)>::isMemberFunction, "");
    static_assert(std::is_same<SFuncInfo<decltype(&MyType::memFunc)>::Ret, bool>::value, "");
}

Этот код компилируется. Но я хочу также обрабатывать случаи с лямбдами. Примерно так:

auto lambda = [](int, bool) -> float { return 3.14f; };

static_assert(SFuncInfo<decltype(lambda)>::isMemberFunction, "");
static_assert(std::is_same<SFuncInfo<decltype(lambda)>::Ret, float>::value, "");

Я попробовал другой вариант. Немногие из перечисленных ниже.

template<class T>
struct SFuncInfo<T, decltype(T())>
{
    static constexpr bool isMemberFunction = true;
};

template<class T>
struct SFuncInfo<T, decltype(&std::decay<decltype(std::declval<T>())>::type::operator())>
{
    static constexpr bool isMemberFunction = true;
};

Это не относится ни к одной из этих специализаций.

Кстати, код ниже также компилируется:

auto lambda = [](int, bool) -> float { return 3.14f; };

using LambdaType = std::decay<decltype(std::declval<decltype(lambda)>())>::type;
using CallOperator = decltype(&LambdaType::operator());
static_assert(std::is_same<SFuncInfo<CallOperator>::Ret, float>::value, "");
static_assert(SFuncInfo<CallOperator>::isMemberFunction, "");

Вот LIVE DEMO - кто-то хочет поиграть.

У кого-нибудь есть хорошее решение для этого?

Ответы [ 2 ]

3 голосов
/ 05 апреля 2019

Добавление этой единственной частичной специализации работает на моей стороне:

template<class Lambda>
struct SFuncInfo<Lambda> : SFuncInfo<decltype(&Lambda::operator())> { };
2 голосов
/ 05 апреля 2019

Решение состоит в том, чтобы создать перегрузку, доступную только для вызываемых типов объектов и наследовать от SFuncInfo с типом operator().

template<typename T>
struct SFuncInfo<T, decltype(void(&T::operator()))> : SFuncInfo<decltype(&T::operator())> {};
//     constraint ----------------^

Однако для поддержки этого я разделил специализации иклассы метаданных, разбивая их на SFuncInfo и SFuncInfoBase:

template<class R, class... FuncParams>
struct SFuncInfoBase
{
    using Signature = R(FuncParams...);
    using Ret = R;

    static constexpr size_t numParams = sizeof...(FuncParams);
};

template<class T, typename = void>
struct SFuncInfo;

// member
template<class T, class Ret, class... Params>
struct SFuncInfo<Ret(T::*)(Params...)const> : SFuncInfo<Ret(T::*)(Params...)> {};

template<class T, class Ret, class... Params>
struct SFuncInfo<Ret(T::*)(Params...)> : SFuncInfoBase<Ret, Params...> 
{
    static constexpr bool isMemberFunction = true;
};

Живой пример

...