【问题标题】:How should I define a std::function variable with default arguments?我应该如何使用默认参数定义 std::function 变量?
【发布时间】:2014-05-19 22:53:26
【问题描述】:

要将 std::function 变量设置为带有默认参数的 lambda 函数,我可以使用 auto,如下所示:

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

这将打印 10。

但我希望 foo 变量驻留在结构中。在结构中我不能使用auto

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

用 std::function 替换 auto

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();

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)>) ()’

没有结构并替换 auto 为 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)>) ()’

那么我应该如何声明 foo?

【问题讨论】:

  • 无法使用默认参数创建std::function

标签: c++ c++11 lambda std-function


【解决方案1】:

std::function 中的签名基于您计划如何调用它,而不是基于您如何构造/分配它。由于您想以两种不同的方式调用它,因此您需要存储到不同的 std::function 对象,如:

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_;
};

或者,您可以自己执行类型擦除(std::function 在幕后执行的操作)以仅存储 lambda 一次,如下所示:

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); }
};

【讨论】:

    【解决方案2】:

    不知道这是否对您有帮助,但您可以将 lambda 存储在模板结构中。

    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();
    

    【讨论】:

      【解决方案3】:

      在 C++20 中,您将能够做到这一点:

      struct foo {
          decltype([](int a = 10){std::cout << a << '\n';}) my_lambda{};
      };
      
      int main() {
          foo f;
          f.my_lambda();
          f.my_lambda(5);
      }
      

      Live on Godbolt

      它看起来确实有点奇怪,但它工作得很好。

      使这成为可能的是use lambdas in unevaluated contextsdefault construct stateless lambdas 的能力。

      【讨论】:

        【解决方案4】:

        您可以解决此问题的一种方法是将您的 std::function 包装在为您实现默认参数的仿函数对象中:

        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();
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-02-22
          • 1970-01-01
          • 1970-01-01
          • 2017-07-31
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多