【问题标题】:Is it possible to use member function call as default argument?是否可以使用成员函数调用作为默认参数?
【发布时间】:2016-07-03 08:57:43
【问题描述】:

这是我的代码:

struct S
{
   int f() { return 1; }
   int g(int arg = f()) { return arg; }
};

int main()
{
    S s;
    return s.g();
}

编译失败,报错:

error: cannot call member function 'int S::f()' without object

尝试this->f() 也不起作用,因为this 可能无法在这种情况下使用。

有没有办法让它工作,仍然使用默认参数?


当然,完全不使用默认参数也可以解决这个问题:

int g(int arg) { return arg; }
int g() { return g(f()); }

但是考虑到在“真实代码”中arg 之前有更多参数,以及遵循此模式的几个函数,这就变得冗长了。 (如果一个函数中有多个默认参数,那就更丑了)。

注意。 This question 起初看起来很相似,但实际上他在问如何形成闭包,这是一个不同的问题(并且链接的解决方案不适用于我的情况)。

【问题讨论】:

  • 应该是int g() { return g(f()); }吧?至少,在真正的代码中,这可能很有意义。当然,在这里它适用于您只有g 中的return 语句。
  • @skypjack 谢谢,已修复

标签: c++ member-functions default-arguments


【解决方案1】:

只有static 的成员才能使用。来自 C++11 标准草案 (n3299),第 8.3.6/9 节:

同样,一个非静态成员不能在默认参数中使用,即使它没有被评估,除非它作为类成员访问表达式的 id-expression (5.2. 5) 或者除非是 用于形成指向成员的指针 (5.3.1)。

例如,这有效:

struct S {
  static int f() { return 1; }
  int g(int arg = f()) { return arg; }
};

int main()
{
  S s;
  return s.g();
}

这也有效(我认为这就是第一个表达式的意思):

struct S {
  int f() { return 42; }
  int g(int arg);
};

static S global;

int S::g(int arg = global.f()) { return arg; }

int main()
{
  S s;
  return s.g();
}

至于this,确实是不允许的(§8.3.6/8):

关键字this不得用于成员函数的默认参数。

cppreference.com 上的default arguments 页面有很多关于该主题的详细信息——它可能变得相当复杂。

【讨论】:

    【解决方案2】:

    如果允许您使用 C++17 中的实验性功能,您可以使用 STL 中的std::optional(有关详细信息,请参阅here)。

    换句话说:

    int g(std::optional<int> oarg = std::optional<int>{}) {
        int arg = oarg ? *oarg : f();
        // go further
    }
    

    编辑

    根据 cmets 的建议,上面的代码在逻辑上应该等同于下面的代码:

    int g(std::optional<int> oarg = std::optional<int>{}) {
        int arg = oarg.value_or(f());
        // go further
    }
    

    这个更具可读性(不是吗?),但请注意它在任何情况下都会执行f
    如果那个功能很昂贵,也许不值得。

    【讨论】:

    • 糟糕,在我的真实代码中,参数是通过 const 引用而不是值来获取的。起初我没有意识到这会有所作为。我认为您的解决方案仍然适用(可以将包装类型更改为 reference_wrapper 或其他内容)
    • @M.M 是的,它仍然适用。无论如何,我添加了另一个答案,它可能提供了一个更好的解决方案(至少是一个起点)。让我知道这是否适合您。
    • 只是一个小建议——我认为在这种情况下应该使用value_or 而不是?:
    • @Predelnik 对,我正在更新答案。谢谢。
    【解决方案3】:

    我添加另一个答案,它与上一个完全不同,可以解决您的问题。
    这个想法是使用另一个类以及显式和非显式构造函数的正确组合。
    它遵循一个最小的工作示例:

    #include <functional>
    #include <iostream>
    
    template<class C, int(C::*M)()>
    struct Arg {
        std::function<int(C*)> fn;
        Arg(int i): fn{[i](C*){ return i; }} { } 
        explicit Arg(): fn{[](C* c){ return (c->*M)(); }} { }
    };
    
    struct S {
        int f() { return 1; }
        int h() { return 2; }
        void g(int arg0,
              Arg<S, &S::f> arg1 = Arg<S, &S::f>{},
              Arg<S, &S::h> arg2 = Arg<S, &S::h>{})
        {
            std::cout << "arguments" << std::endl;
            std::cout << "arg0: " << arg0 << std::endl;
            std::cout << "arg1: " << arg1.fn(this) << std::endl;
            std::cout << "arg2: " << arg2.fn(this) << std::endl;
        }
    };
    
    int main() {
        S s{};
        s.g(42, 41, 40);
        s.g(0);
    }
    

    该示例显示了如何混合使用默认参数和非默认参数。
    修改它并让g 成为一个具有空参数列表的函数非常简单,就像在原始问题中一样。
    我也很确定可以改进示例并以更好的方式结束,无论如何这应该是一个很好的起点。

    它遵循问题中应用于原始示例的解决方案:

    #include <functional>
    
    template<class C, int(C::*M)()>
    struct Arg {
        std::function<int(C*)> fn;
        Arg(int i): fn{[i](C*){ return i; }} { } 
        explicit Arg(): fn{[](C* c){ return (c->*M)(); }} { }
    };
    
    struct S {
        int f() { return 1; }
        int g(Arg<S, &S::f> arg = Arg<S, &S::f>{}) {
            return arg.fn(this);
        }
    };
    
    int main() {   
        S s{}; 
        return s.g();
    }
    

    仅此而已,即使没有static 方法或全局变量,也可以这样做。
    当然,我们可以以某种方式使用我们的 this。这是一个稍微弯曲语言的问题......

    【讨论】:

      猜你喜欢
      • 2015-01-15
      • 2011-06-21
      • 2020-11-27
      • 1970-01-01
      • 2012-08-13
      • 2016-10-28
      • 1970-01-01
      相关资源
      最近更新 更多