Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How should I define a std::function variable with default arguments?

To set a std::function variable to a lambda function with default argument I can use auto as in:

auto foo = [](int x = 10){cout << x << endl;};
foo();

This will print 10.

But I want the foo variable to reside in a struct. In a struct I cannot use auto.

struct Bar
{
    auto foo = [](int x = 10}(cout << x << endl}; //error: non-static data member declared ‘auto’
};
Bar bar;
bar.foo();

Replacing auto with std::function

struct Bar
{
    std::function<void(int x = 10)> foo = [](int x = 10}(cout << x << endl}; //error: default arguments are only permitted for function parameters
};
Bar bar;
bar.foo();

or

struct Bar
{
    std::function<void(int)> foo = [](int x = 10}(cout << x << endl};
};
Bar bar;
bar.foo(); //error: no match for call to ‘(std::function<void(int)>) ()’

Without the struct and replacing auto for std::function:

std::function<void(int x)> foo = [](int x = 10){cout << x << endl;};
foo(); //error: no match for call to ‘(std::function<void(int)>) ()’

So how should I declare foo?

like image 923
wibek Avatar asked Apr 07 '14 15:04

wibek


People also ask

How can we pass default arguments to a function?

In C++ programming, we can provide default values for function parameters. If a function with default arguments is called without passing arguments, then the default parameters are used. However, if arguments are passed while calling the function, the default arguments are ignored.

Can functions have default arguments?

Of the operators, only the function call operator and the operator new can have default arguments when they are overloaded. You can supply any default argument values in the function declaration or in the definition.

What do you mean by function with default arguments?

In computer programming, a default argument is an argument to a function that a programmer is not required to specify. In most programming languages, functions may take one or more arguments. Usually, each argument must be specified in full (this is the case in the C programming language).

Which case default argument passing in function is not allowed?

Default arguments are only allowed in the parameter lists of function declarations and lambda-expressions, (since C++11) and are not allowed in the declarations of pointers to functions, references to functions, or in typedef declarations.


3 Answers

The signature in std::function is based on how you plan to call it and not on how you construct/assign it. Since you want to call it two different ways, you'll need to store to different std::function objects, as in:

struct Call
{
    template<typename F>
    explicit Call(F f) : zero_(f), one_(std::move(f)) {}

    void operator()() { zero_(); }
    void operator()(int i) { one_(i); }

    std::function<void()>    zero_;
    std::function<void(int)> one_;
};

Alternatively, you can do the type erasure yourself (what std::function does behind the scenes) to only store the lambda once, as in:

class TECall
{
    struct Concept
    {   
        Concept() = default;
        Concept(Concept const&) = default;
        virtual ~Concept() = default;

        virtual Concept* clone() const = 0;

        virtual void operator()() = 0;
        virtual void operator()(int) = 0;
    };  

    template<typename T>
    struct Model final : Concept
    {   
        explicit Model(T t) : data(std::move(t)) {}
        Model* clone() const override { return new Model(*this); }

        void operator()() override { data(); }
        void operator()(int i) override { data(i); }

        T data;
    };  

    std::unique_ptr<Concept> object;

public:
    template<typename F>
    TECall(F f) : object(new Model<F>(std::move(f))) {}

    TECall(TECall const& that) : object(that.object ? that.object->clone() : nullptr) {}
    TECall(TECall&& that) = default;
    TECall& operator=(TECall that) { object = std::move(that.object); return *this; }

    void operator()() { (*object)(); }
    void operator()(int i) { (*object)(i); }
};
like image 170
Nevin Avatar answered Oct 11 '22 16:10

Nevin


One way you could solve this would be to wrap your std::function in a functor object which implements the default arguments for you:

struct MyFunc
{
    void operator()(int x = 10) { f(x); }
    std::function<void(int x)> f;
};

struct Bar
{
    MyFunc foo = {[](int x){std::cout << x << "\n";}};
};

int main() {
    Bar bar;
    bar.foo();
}
like image 33
Alan Birtles Avatar answered Oct 11 '22 15:10

Alan Birtles


Don't know if that will help you, but you can store a lambda in a templated struct.

template <typename F>
struct Bar {
  F foo;
  Bar (F fun): foo (std::move (fun)) {}
};

auto f = [](int x = 10) {cout << x << endl;};
Bar<decltype (f)> bar (f);
bar.foo();

auto makeFun = [](){return [](int x = 10) {cout << x << endl;};};

Bar<decltype (makeFun())> bar2 (makeFun());
bar2.foo();
like image 29
ArtemGr Avatar answered Oct 11 '22 16:10

ArtemGr