Я ищу правила передачи функций шаблонов C ++ в качестве аргументов.
Это поддерживается C ++, как показано на примере здесь:
#include <iostream>
void add1(int &v)
{
v+=1;
}
void add2(int &v)
{
v+=2;
}
template <void (*T)(int &)>
void doOperation()
{
int temp=0;
T(temp);
std::cout << "Result is " << temp << std::endl;
}
int main()
{
doOperation<add1>();
doOperation<add2>();
}
Однако узнать об этой технике сложно. Поиск в Google для «функции в качестве аргумента шаблона» мало что дает. И классические шаблоны C ++ The Complete Guide, к удивлению, также не обсуждают это (по крайней мере, из моего поиска).
У меня есть вопросы, является ли это C ++ (или просто каким-либо широко поддерживаемым расширением).
Кроме того, есть ли способ позволить функтору с одинаковой сигнатурой взаимозаменяемо использоваться с явными функциями во время такого вызова шаблона?
Следующее не работает в вышеуказанной программе, по крайней мере, в Visual C ++ , потому что синтаксис явно неправильный. Было бы неплохо иметь возможность отключить функцию для функтора и наоборот, подобно тому, как вы можете передать указатель функции или функтор в алгоритм std :: sort, если вы хотите определить пользовательскую операцию сравнения.
struct add3 {
void operator() (int &v) {v+=3;}
};
...
doOperation<add3>();
Будем благодарны за указатели на одну или две веб-ссылки или страницу в книге шаблонов C ++!
источник
-std=gnu++17
. Могу ли я использовать результат оператора захвата лямбда-констекспра в C ++ 17 без захвата в качестве аргумента не тип шаблона указателя функции? ,Ответы:
Да, это действительно.
Что касается работы с функторами, обычное решение выглядит примерно так:
который теперь можно назвать как:
Смотрите это в прямом эфире
Проблема в том, что если компилятору сложно встроить вызов
add2
, поскольку компилятор знает, что тип указателя на функциюvoid (*)(int &)
передаетсяdoOperation
. (Ноadd3
, будучи функтором, его можно легко встроить. Здесь компилятор знает, чтоadd3
в функцию передается объект типа , а это означает, что вызывается функцияadd3::operator()
, а не просто какой-то неизвестный указатель на функцию.)источник
template <typename F> void doOperation(F&& f) {/**/}
), поэтому bind, например, может передать выражение bind вместо его связывания?Параметры шаблона могут быть параметризованы по типу (typename T) или по значению (int X).
«Традиционный» C ++ способ шаблонирования фрагмента кода заключается в использовании функтора, то есть кода в объекте, и объект, таким образом, дает коду уникальный тип.
При работе с традиционными функциями этот метод не работает должным образом, потому что изменение типа не указывает на конкретную функцию - скорее, оно указывает только сигнатуру многих возможных функций. Так:
Не эквивалентно случаю функтора. В этом примере do_op создается для всех указателей на функции, чья сигнатура int X (int, int). Компилятор должен быть довольно агрессивным, чтобы полностью встроить этот случай. (Я не исключаю, хотя, поскольку оптимизация компилятора стала довольно продвинутой.)
Один из способов сказать, что этот код не совсем то, что мы хотим, это:
все еще законно, и ясно, что это не становится ограниченным. Чтобы получить полное встраивание, нам нужно шаблон по значению, чтобы функция полностью была доступна в шаблоне.
В этом случае каждая конкретная версия do_op создается с уже доступной определенной функцией. Таким образом, мы ожидаем, что код для do_op будет очень похож на «return a + b». (Программисты на Лиспе, перестань ухмыляться!)
Мы также можем подтвердить, что это ближе к тому, что мы хотим, потому что это:
не удастся скомпилировать. GCC говорит: «ошибка:« func_ptr »не может появляться в константном выражении. Другими словами, я не могу полностью развернуть do_op, потому что вы не дали мне достаточно информации во время компиляции, чтобы знать, что такое наш оператор».
Так что, если второй пример действительно полностью отражает нашу операционную систему, а первый - нет, что хорошего в шаблоне? Что это делает? Ответ: тип принуждения. Этот рифф на первом примере будет работать:
Этот пример будет работать! (Я не утверждаю, что это хороший C ++, но ...) То, что произошло, это do_op был создан вокруг подписей различных функций, и каждое отдельное создание будет писать код приведения разных типов. Таким образом, инстанцированный код для do_op с fadd выглядит примерно так:
Для сравнения, наш случайный случай требует точного соответствия аргументов функции.
источник
int c = do_op(4,5,func_ptr);
«явно не вписывается».Указатели на функции могут быть переданы в качестве параметров шаблона, и это является частью стандарта C ++ . Однако в шаблоне они объявлены и используются как функции, а не как указатель на функцию. При создании шаблона один передается адрес функции , а не только имя.
Например:
Если вы хотите передать тип функтора в качестве аргумента шаблона:
Несколько ответов передают экземпляр функтора в качестве аргумента:
Самое близкое к этому единообразному появлению с аргументом шаблона является определение
do_op
дважды - один раз с параметром нетипичного типа и один раз с параметром типа.Честно говоря, я действительно ожидал, что это не скомпилируется, но у меня это сработало с gcc-4.8 и Visual Studio 2013.
источник
В вашем шаблоне
Параметр
T
является нетиповым параметром шаблона. Это означает, что поведение функции шаблона изменяется со значением параметра (которое должно быть зафиксировано во время компиляции, какие константы указателя функции являются).Если вы хотите что-то, что работает как с объектами функций, так и с параметрами функций, вам нужен типизированный шаблон. Однако, когда вы делаете это, вам также необходимо предоставить экземпляр объекта (либо экземпляр объекта функции, либо указатель функции) для функции во время выполнения.
Есть некоторые незначительные соображения производительности. Эта новая версия может быть менее эффективной с аргументами указателя на функцию, поскольку указатель на конкретную функцию только разыменовывается и вызывается во время выполнения, тогда как шаблон указателя на функцию можно оптимизировать (возможно, вызов функции встроен) на основе конкретного используемого указателя функции. Функциональные объекты часто могут быть очень эффективно расширены с помощью типизированного шаблона, хотя конкретное
operator()
полностью определяется типом функционального объекта.источник
Причина, по которой ваш пример функтора не работает, заключается в том, что вам нужен экземпляр для вызова
operator()
.источник
Редактировать: передача оператора в качестве ссылки не работает. Для простоты понимаем его как указатель на функцию. Вы просто отправляете указатель, а не ссылку. Я думаю, что вы пытаетесь написать что-то вроде этого.
, ,
источник