Если вы хотите MyClass
быть шаблон, который может содержать как свободную функцию указателей типов:
double (*)(const T &var);
bool (*)(const T &var);
для некоторых типов параметров T
, или в качестве альтернативы члена-функции указателей типов:
double (C::*)(const T &var);
bool (C::*)(const T &var);
для некоторых типов параметров C
и T
то, MyClass
должны быть настроены параметры обоими T
и C
и вам потребуется две специализации:
- Где
C
некоторый тип неклассовых
- Где
C
любого типа
класс в случае (1), тип non-class C
не может иметь функции-члены, , чтобы реализовать специализацию указателя свободной функции.
В случае (2) класс C
может быть таким, который имеет функции-члены, так что один будет реализовывать специализацию указателя-функции-члена.
Очевидным выбором для неклассового типа C
является void
. Таким образом, мы можем сделать C
по умолчанию void
:
Первичный шаблон
template<typename T, typename C = void>
struct MyClass;
Так что:
MyClass<T>
будет свободный указатель функции специализации для T
и:
MyClass<T,C>
для любых C
кроме void
, будет специалистом-указателем-членом.
Как вы знаете, вы можете использовать std::enable_if
и SFINAE
сделать компилятор выбрал одну специализацию шаблона класса или другой, в зависимости от параметров ли один его шаблон U
удовлетворяет некоторые испытания compiletime.Вы можете взять такой подход здесь, а другой один доступен, не требует, чтобы устройство:
Начиная с основным шаблоном, мы просто хотели бы иметь:
Бесплатной функция специализация
template<typename T>
struct MyClass<T>
{
... for free function pointers ...
};
и:
функция член специализация
template<typename T, typename C>
struct MyClass<T,C>
{
... for member function pointers ...
};
Но у нас не может быть только этого, потому что функция «специализация»-члена имеет ровно те же параметры шаблона, что и основной шаблон. Это означает, что не специализация, и компилятор не допустит этого.
Вы можете легко удалить эту проблему, однако, просто указав основной шаблон еще одним параметром шаблона по умолчанию, который ему не нужен, но наличие позволяет обеим специализациям стоять.
Новый первичный шаблон
template <typename T, typename C = void, typename Default = void>
struct MyClass;
Так вот иллюстративное решение:
// Primary template
template <typename T, typename C = void, typename Default = void>
struct MyClass;
// Free function specialization
template <typename T>
struct MyClass<T>
{
using firstFunctor_t = double(*)(T const &);
using secondFunctor_t = bool(*)(T const &);
MyClass(firstFunctor_t firstFunc, secondFunctor_t secondFunc)
: _firstFunc(firstFunc),
_secondFunc(secondFunc)
{}
double callFirst(T const & var) {
return _firstFunc(var);
}
bool callSecond(T const & var) {
return _secondFunc(var);
}
private:
firstFunctor_t _firstFunc;
secondFunctor_t _secondFunc;
};
// Member function specialization
template <typename T, typename C>
struct MyClass<T,C>
{
using firstFunctor_t = double(C::*)(T const &);
using secondFunctor_t = bool(C::*)(T const &) const;
MyClass(firstFunctor_t firstFunc, secondFunctor_t secondFunc)
: _firstFunc(firstFunc),
_secondFunc(secondFunc)
{}
double callFirst(C & obj, T const & var) {
return (obj.*_firstFunc)(var);
}
double callFirst(C const & obj, T const & var) {
auto & o = const_cast<C&>(obj);
return (o.*_firstFunc)(var);
}
bool callSecond(C & obj, T const & var) {
return (obj.*_secondFunc)(var);
}
bool callSecond(C const & obj, T const & var) {
auto & o = const_cast<C&>(obj);
return (o.*_secondFunc)(var);
}
private:
firstFunctor_t _firstFunc;
secondFunctor_t _secondFunc;
};
В функции специализации членов, обратите внимание на несколько моментов, которые вы могли бы не рассматривали: -
Я решил, что вторая функция-член, которую я хочу сохранить, должна быть описание товара const участник функция. Более чем вероятно, что функция-член C
, которая принимает аргумент T const &
и возвращает bool, будет членом const
, не правда ли? И если да, то, что const-ness
должен быть частью определения типа член-функция, которую я использую в специализации:
using secondFunctor_t = bool(C::*)(T const &) const;
или пытается создать экземпляр специализации с любой bool (C::*)(T const &) const
не будет компилироваться.
Кроме того, я предоставил две перегрузки для каждого из MyClass<T,C>::callFirst
и MyClass<T,C>::callSecond
, один с аргументами:
C & obj, T const & var
, а другой с аргументами:
C const & obj, T const & var
Без второй попытки вызвать либо MyClass<T,C>::callFirst
или MyClass<T,C>::callSecond
с obj
, который будет const, не будет скомпилировать.
Для программы демонстрационного этого решения, которое вы можете добавить:
#include <iostream>
#include <string>
double foo(std::string const & s)
{
return std::stod(s);
}
bool bar(std::string const & s)
{
return s.size() > 0;
}
struct SomeClass
{
SomeClass(){};
double foo(std::string const & s) {
return ::foo(s);
}
bool bar(std::string const & s) const {
return ::bar(s);
}
};
int main()
{
MyClass<std::string> my0{foo,bar};
std::cout << std::boolalpha;
std::cout << my0.callFirst("1.11") << std::endl;
std::cout << my0.callSecond("Hello World") << std::endl;
MyClass<std::string,SomeClass> my1{&SomeClass::foo,&SomeClass::bar};
SomeClass thing;
std::cout << my1.callFirst(thing,"2.22") << std::endl;
std::cout << my1.callSecond(thing,"Hello World") << std::endl;
SomeClass const constThing;
std::cout << my1.callFirst(constThing,"3.33") << std::endl;
std::cout << my1.callSecond(constThing,"Hello World") << std::endl;
return 0;
}
See it live
Вы сказали, что вы хотите этот шаблон, чтобы быть «чрезвычайно гибким». Иллюстративное решение установлено на ваш пример, но вы можете быть заинтересованы в том, чтобы знать, что это не почти так же гибко, как вы могли бы получить. . Для бесплатных функций и функций-членов, с дополнительными параметрами variadic template , ваш шаблон может хранить и вызывать функции [member] с условными типами возврата и произвольными числами аргументов произвольных типов. См. this question и ответ.
Проблема с «указателем функции на произвольный класс» заключается в том, что вам нужен «объект произвольного класса» для его использования. И нет, 'void *' не будет работать. Вероятно, вы закончите с 'std :: bind' и' std :: function', потому что у этого больше нет зависимости от бесконечного множества произвольных классов. – MSalters