2

I got "a nonstatic member reference must be relative to a specific object" with the following code,

class A{
    int data;
    int _default = 0;
public:
    A(int data, int _default) : data(data), _default(_default) {}
    void set(int data = _default){ this->data = data; }
};

int main(){
    A a1(1, 1); a1.set(10); a1.set();
    A a2(2, 2); a2.set(20);
}

What I want to do?

  • when we call A.set(), A::data is set to A::_default
  • when we call A.set(a), A::data is set to a
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
LI.LE
  • 97
  • 5

3 Answers3

6

Non-static data members can't be used as default arguments, which are supposed to be provided from the caller side context.

Non-static class members are not allowed in default arguments (even if they are not evaluated),

You could provide another overload.

class A{
    int data;
    int _default = 0;
public:
    A(int data, int _default) : data(data), _default(_default) {}
    void set(int data){ this->data = data; }
    void set(){ set(this->_default); }
};
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • Before asking this question, I deleted the overload version. Actually, what I want to do is removing the overloads. – LI.LE Jul 13 '21 at 05:43
  • @LI.LE It's just impossible. Unless making `_default` a `static` member, or call it like `a1.set(a1._default);`. – songyuanyao Jul 13 '21 at 05:45
  • 4
    @LI.LE consider that default arguments are second class citizens. They arent as nice as one might expect. They are replaced at the call site, ie you cannot use a private member as default – 463035818_is_not_an_ai Jul 13 '21 at 05:46
  • 1
    _"Actually, what I want to do is removing the overloads."_ — Why? – Daniel Langr Jul 13 '21 at 06:54
5

From the 2017 C++ standard, section 11.3.6, para 9

A non-static member shall not appear in a default argument unless it appears as the id-expression of a class member access expression (8.2.5) or unless it is used to form a pointer to member (8.3.1).

All other C++ standards (before and since) have a similar statement, although the wording and section numbers vary. Neither of the "unless" points in the above are applicable in this case.

It is notionally possible (since C++17) to use std::optional (from header <optional>) to avoid the overload, as follows.

#include <optional>

class A
{
     int data;
     int _default = 0;
   public:
     // other members omitted
   
     void set(std::optional<int> data = std::optional<int>())
     {
         this->data = data.has_value() ? data.value() : _default;
     };
 };

or (as suggested by Jarod42 in comments)

     void set(std::optional<int> data = std::nullopt)
     {
         this->data = data.value_or(_default);
     };

I use the word "notionally" in the above because, personally, I would simply use two overloads. It is (arguably) easier for a mere mortal to understand and works with all C++ standards (not just since C++17)

 void set(int data){ this->data = data; }
 void set(){ set(this->_default); }       
Peter
  • 35,646
  • 4
  • 32
  • 74
  • [`std::nullopt`](https://en.cppreference.com/w/cpp/utility/optional/nullopt) and [`optional::value_or`](https://en.cppreference.com/w/cpp/utility/optional/value_or) might help – Jarod42 Jul 13 '21 at 08:57
  • 1
    ie. `void set(std::optional arg = std::nullopt) { data = arg.value_or(_default); }`. – Jarod42 Jul 13 '21 at 08:59
  • @Jarod42 Yes, that's a viable alternative as well. I'll edit shortly to note that. – Peter Jul 13 '21 at 10:39
3

what you can do is you can provide an overload.

void set(){ this->data = this->_default; }
void set(int data){ this->data = data; }

This will accomplish the job you want to do.

foragerDev
  • 1,307
  • 1
  • 9
  • 22