у меня простой класс
template <typename T>
using ConversionFunction = T(*)(T val);
static int Foo1(int x)
{
return x * x;
}
static double Foo1(double x)
{
return x * x + 1;
}
struct Foo
{
ConversionFunction<double> d;
ConversionFunction<int> i;
ConversionFunction<float> f;
template <typename T>
void assign(){
//??
}
};
Можно ли написать что-то вроде этого:
Foo f;
f.assign<Foo1>();
а метод assign
автоматически получит указатели на все существующие методы с именем Foo1
? Что-то вроде этого:
template <typename T>
void assign(){
d = if exist(T for double) ? &(T for double) : nullptr;
i = if exist(T for int) ? &(T for int) : nullptr;
f = if exist(T for float) ? &(T for float) : nullptr;
}
Нет, C++ просто так не работает на фундаментальном уровне.
Я не очень понимаю этот вопрос. Какую проблему вы пытаетесь решить - можете ли вы предоставить больше контекста?
@PaulSanders Я хотел, чтобы пользователь указал только имя метода, а класс сгенерировал бы все существующие назначения. Поэтому, если я добавлю новый метод, мне придется изменить только одно место, а не везде в коде, где используется Foo.
Я не уверен, почему ты хочешь это сделать. общий вызываемый объект (BOOST_HOF_LIFT
) решает проблему?
Это не C, а C++; вместо трех отдельных функций существует один класс с тремя методами. Тогда этот вид задания становится тривиальным. Параметр шаблона — это класс.
@SamVarshavchik Можете ли вы дать ответ с примером кода?
Вы не можете просто использовать имя для передачи набора перегрузок.
Альтернативой является обернуть его внутри класса/лямбда:
template <typename T>
struct to_function_pointer
{
template <typename U>
operator ConversionFunction<U> () const { return [](U x){ return T{}(x); }; }
};
const auto foo_caller = to_function_pointer<decltype([](auto x)-> decltype(Foo1(x)){ return Foo1(x); })>();
Имена не могут передаваться в системе типов C++. Это главный источник боли во многих проблемах, но это именно так. Вы можете передавать значения, или в случае шаблонов: типы и другие шаблоны. Но имена должны быть преобразованы в значение или тип, прежде чем они будут «передаваться» в любом контексте.
Единственным (своего рода) исключением является препроцессор, поскольку он работает с токенами, вы можете использовать его для генерации новых значений и типов из имен. Если это не то, что вызывает у вас тошноту, вы можете создать эту сантехнику на C++ 20 и более поздних версиях.
#include <type_traits>
#include <cassert>
#define LIFT_ADDRESS(name) \
[]<typename T>(std::type_identity<T>) -> T(*)(T) \
requires requires(T(*ptr)(T)) { ptr = name; } { \
return name; \
}
template <typename T>
using ConversionFunction = T(*)(T);
static int Foo1(int x)
{
return x * x;
}
static double Foo1(double x)
{
return x * x + 1;
}
struct Foo
{
ConversionFunction<double> d{};
ConversionFunction<int> i{};
ConversionFunction<float> f{};
template <class Lifted>
void assign(Lifted l) {
if constexpr(requires { l(std::type_identity<double>{}); })
d = l(std::type_identity<double>{});
if constexpr(requires { l(std::type_identity<int>{}); })
i = l(std::type_identity<int>{});
if constexpr(requires { l(std::type_identity<float>{}); })
f = l(std::type_identity<float>{});
}
};
int main() {
Foo f;
f.assign(LIFT_ADDRESS(Foo1));
assert(f.d);
assert(f.i);
assert(!f.f);
}
Макрос на самом деле просто средство для преобразования имен наборов перегрузки в новый функциональный объект (отсюда и номенклатура «ПОДЪЕМ»). Получив это, вы можете передавать его в функции (и шаблоны функций), а также проверять его свойства в выражении require (при условии, что оно удобно для sfiane).
Общая лямбда просто принимает тег, который передает тип, добавляет требование, чтобы указатель на функцию указанного типа мог быть назначен из «имени» (так что return
также действителен), чтобы быть дружественным к sfinae, и это все. Тело assign
просто использует набор обязательных выражений, чтобы проверить, можно ли вызвать этот удобный для сифней функтор, и если да, то он вызывает его.
Многое из вышеперечисленного можно эмулировать в более ранних стандартах вплоть до С++ 14 (общая лямбда — это самый минимум, как я полагаю), хотя реализации становятся все менее приятными.
Вы просите обдумать, что С++ в настоящее время не поддерживает.