#c #function #function-pointers #default-arguments
#c #функция #функции-указатели #по умолчанию-аргументы
Вопрос:
Итак, я знаю, что вы можете передать функцию в качестве аргумента следующим образом:
int a(int x) {
return x 1;
}
int b(int (*f)(int), int x) {
return f(x); // returns x 1
}
Я также знаю, что у вас может быть функция с аргументами по умолчанию, например:
int a(int x = 1) {
return x;
}
a(2); // 2
a(); // 1
Однако, как мне передать функцию с аргументами по умолчанию в функцию и сохранить это поведение?
Я попробовал следующее:
int b(int (*f)(int), int x) {
f(x); // works as expected
f(); // doesn't work because there aren't enough arguments to f
}
и
int b(int (*f)()) {
f(); // doesn't work because it cannot convert int (*)(int) to int (*)()
}
Комментарии:
1. Это будет сложно, поскольку аргументы по умолчанию являются операцией времени компиляции. Компилятор, увидев определение
int a(int x = 1)
, превратит вашеa();
вa(1);
и скомпилирует это .2. аргументы по умолчанию применяются на сайте вызова во время компиляции на основе того, что может видеть компилятор. Если компилятор не может быть на 100% уверен, какая функция находится на другом конце этого указателя функции, практически невозможно применить аргумент по умолчанию. Даже если используется только одна функция, эта функция может иметь разные объявления в разных точках кода с разными аргументами или значениями по умолчанию для этих аргументов. Из-за этого я не удивлюсь, если компилятор даже не попытается разобраться.
3. @DrewDormann О, я вижу, это имеет смысл. Спасибо, я, вероятно, обойду это, создав вместо этого вызываемый объект.
4. Вызываемые объекты @hyper-neutrino должны быть первым вариантом в C по указателям на функции, IMO. Указатели на функции не передают состояние, и вы ничего не можете с ними сделать, кроме того, что в принципе
C
можно сделать. С вызываемым объектом вы в принципе можете делать все, что угодно, включая перемещение всего определения параметров с сайта вызова, все из-заoperator()
. Это то, что вы не можете сделать с «тупыми» указателями на функции.5. Параметр по умолчанию не является частью подписи. Таким образом, вы не можете различать, например,
void f(int=0)
иvoid g(int=1)
. Вам нужно будет обернуть функцию вместе с параметром по умолчанию.
Ответ №1:
Невозможно пересылать значения параметров по умолчанию с помощью указателей на функции.
Но вы можете заставить ее работать, если сможете превратиться b
в шаблонную функцию:
Используя пакет параметров для operator()
и явно вызывая a
, мы даем компилятору возможность применить значения аргументов по умолчанию, если это необходимо:
int a(int x = 12) {
return x 1;
}
template<class T>
int b(T f, int x) {
return f() f(x);
}
struct AFnWrapper {
template<class... Args>
auto operator()(Argsamp;amp;... args) {
return a(std::forward<Args>(args)...);
}
};
int main() {
std::cout << b(AFnWrapper{}, 1) << std::endl; // 15
}
Более короткой версией этого было бы просто использовать лямбда-выражение:
std::cout << b([](autoamp;amp;... args) { return a(std::forward<decltype(args)>(args)...); }, 1) << std::endl;
Если вам не нужна идеальная пересылка, вы можете сделать ее еще короче:
std::cout << b([](auto... args) { return a(args...); }, 1) << std::endl;
Ответ №2:
Это невозможно сделать с помощью указателей на функции, поскольку указатели на функции не имеют возможности хранить информацию об аргументах по умолчанию. Однако вы можете передать объект function, который имеет функцию-член с аргументами по умолчанию.
Преобразование a
в такой объект простое; просто сделайте его лямбда
auto a = [](int x = 1) {
// ...
};
и затем b
может принимать объект функции, где выводится тип
template<typename F>
int b(F f, int x) {
f(x); // ok
f(); // also ok, default value is 1
}
Вот демонстрация.