функция с аргументами по умолчанию в качестве аргумента

#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;
 

пример godbolt

Ответ №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
}
 

Вот демонстрация.