Я создаю класс стираемого типа TypeErased
, используя std::variant
, который может быть целым числом, комплексным числом, вектором или ошибкой.
Пока я создавал оператор сложения (который использует std::visit
), я столкнулся с проблемой: я не хочу, чтобы определенные типы складывались вместе, и кажется очень непрактичным создавать перегрузки для каждой отдельной нежелательной комбинации.
Например, он должен иметь возможность складывать целое число с комплексным числом, но не вектор и целое число. Для этого я попытался использовать SFINAE (я думаю) для создания структуры, которая может определить, существует ли перегрузка функции:
#include<iostream>
#include<string>
void print(int) {}
template<auto FuncPtr, typename ...Args>
struct FuncExists
{
template<typename = decltype(FuncPtr(std::declval<Args>()...))>
constexpr static std::true_type test(int);
constexpr static std::false_type test(...);
constexpr static bool value = decltype(test(0))::value;
};
int main()
{
std::cout<<"print string exists: "<<FuncExists<print, std::string>().value;
}
Я ожидал, что он сможет определить, действителен он или нет, но всякий раз, когда я пытаюсь проверить его на чем-то, что не должно работать, компилятор выдает ошибку. Когда я пытаюсь пройти std::string
:
error: cannot convert 'std::__cxx11::basic_string<char>' to 'int' in argument passing
12 | constexpr static auto test(int) -> decltype(FuncPtr(std::declval<Args>()...), std::true_type());
Почему это не работает и как это изменить, чтобы оно работало?
Для ясности я пытаюсь выяснить, существует ли определенная перегрузка для ЛЮБОЙ функции (например, оператора+, греха и т. д.). Поэтому, если я попытаюсь добавить вариант с целым числом и вариант с вектором, он может вызвать исключение.
вы говорите о добавлении, затем показываете код метода print
. Вы хотите проверить произвольные функции или просто проверить, действителен ли a + b
для определенного типа a
и b
?
Я уточню это в вопросе
также я хочу проверить, существует ли какая-либо перегрузка функции для какой-либо функции, например, оператора +, греха и т. д., с любым количеством перегрузок или параметров.
Можете ли вы использовать C++20?
К тому времени, когда у вас есть указатель на функцию, разрешение перегрузки (хотя, возможно, и тривиальное) уже произошло. Обычный трюк — использовать лямбды (возможно, с decltype
, особенно до C++20), но это сильно отличается от того, что у вас было до сих пор.
(Я могу использовать С++ 20). Понятно, я провел кое-какие исследования по этому поводу, но, тем не менее, я все еще получаю нежелательные сообщения об ошибках компилятора :(
SFINAE происходит только в «прямом» контексте. Здесь шаблоны фиксируются классом, и это становится серьезной ошибкой.
Поскольку у вас C++20, вы можете использовать requires
:
template <typename T>
concept is_printable = requires(T t) { print(t); };
Код в операции — это проблема xy. Они действительно хотят увидеть, существует ли двоичная функция для двух, возможно, разных типов. Изменить, я думаю, это как унарное (sin(x)
), так и двоичное.
@AndyG: Аналогичный ответ: template <typename T, typename U> concept is_some_func_possible = requires(T t, U u) { some_func(t, u); };
.
Я пытался показать пример с +
(со смешанными типами), но int
нельзя добавить к std::complex
:-/
Учитывая ограничения дизайна OP (вариант фиксированного типа), возможно, имеет смысл записать явные перегрузки посетителей для операций, которые их интересуют.
А учитывая, что они заинтересованы в этих операциях только для подмножества комбинаций типов, содержащихся в варианте, тип результата их операции должен отличаться от типа результата обернутой операции (для учета несовместимых типов), что приводит например, другой вариант. Если только не захотят кинуть, но у меня такое впечатление, что ОП играет с ФП.
Вы можете добавить двойной и сложный <дабл>
Привет, ОП, это означает, что мне придется вручную выполнять все операции (sin cos sqrt plus и т. д.)?
Кроме того, у меня есть вопрос. Нет ли общего способа проверить, существует ли перегрузка функции? По моему мнению, это довольно простая задача для компилятора.
Либо с концепцией/особенностями, либо «встроенными», где вам это нужно.
ок, немного разочаровывает, лол, в любом случае спасибо за ответ :D
Вы не можете передать «набор перегрузки», поэтому вам придется использовать МАКРОС для передачи «имени» или обернуть набор перегрузки в функтор (например, лямбда: [](auto...args)->sin(args...){ return sin(args...); }
.
С requires
обнаружение идиомы — это всего лишь одна строка, а не целая структура для копирования и вставки ;-)
Спасибо, чувак, теперь мне придется узнать об этих концепциях. Они кажутся крутыми
Хотите ли вы, чтобы перегрузка не компилировалась, или было бы приемлемо, чтобы она что-то делала (например, не выполняла операции или просто возвращала один аргумент) и просто не использовалась?