4

I tried to set a default parameter to a member variable, it gave me this bug.

[cquery] invalid use of non-static data member 'num'

code

#include <iostream>
class Test{
   private:
      int val = 0;
   public:
      void print_num(int num = val){
         std::cout << num << '\n';
      }
}
int main(){
   Test test;
   test.print_num();
   return 0;
}

3 Answers3

3

Despite it being quite obvious what this would mean, you just can’t. The usual workaround is to provide an overload that (implicitly) uses this to get the member value.

Davis Herring
  • 36,443
  • 4
  • 48
  • 76
2

In order to set a default value for a function parameter to a class member, the member must be static.

Citing the documentation,

Non-static class members are not allowed in default arguments

However, given the context of your code sample I doubt this is what you want to do (as every instance of Test class will point to the same val. Citing the docs:

Static members of a class are not associated with the objects of the class: they are independent variables

If you make val static in your example, and have multiple instances of Test in use by your code, you can (and very likely will) have some unexpected behavior. Consider:

#include <iostream>
class Test{
   private:
      static int val;
   public:
      void print_num(int num = val){
         std::cout << num << '\n';
      }
      void set_val(int num) {
          val = num;
      }
}
int Test::val = 0;
int main(){
   Test test1;
   test1.set_val(1);
   Test test2;
   test2.set_val(2);
   test1.print_num(); // results in "2"
   return 0;
}

A better alternative would be to pass a pointer to your function like this:

#include <iostream>
class Test{
   private:
      int val = 0;
   public:
      void print_num(int* numptr = nullptr){
         int num = (numptr ? *numptr : val);
         std::cout << num << '\n';
      }
}
int main(){
   Test test;
   test.print_num();
   return 0;
}

Or, as described by Davis Herring, use an overload:

#include <iostream>
class Test{
   private:
      int val = 0;
   public:
      void print_num(int num){
         std::cout << num << '\n';
      }
      void print_num() {
        return print_num(val); // return is irrelevant here, but my preferred coding style
      }
}
int main(){
   Test test;
   test.print_num();
   return 0;
}

Edited to reflect the comment and example.

LVB
  • 163
  • 1
  • 8
  • 1
    *In order to set a default value for a function parameter the value must be known at compile time* [Not true](https://en.cppreference.com/w/cpp/language/default_arguments). – user4581301 Nov 18 '21 at 20:10
  • Example: https://godbolt.org/z/Tz5KqKKoY – user4581301 Nov 18 '21 at 20:12
  • @user4581301: Perhaps more interesting, you can call a function, even one which creates a new value out of thin air, in the default argument specification. However, that doesn't help OP much because you have no access to `this` through which you might access `val`. – John Zwinck Nov 18 '21 at 20:15
  • @JohnZwinck I figured that was the problem, but my Standard diving didn't explain why you can't do it, just that you can't. – user4581301 Nov 18 '21 at 20:18
  • @user4581301 I think this is an even better example that the value can be evaluated at run time: http://cpp.sh/5gdzs (try entering 42 from the standard input and see the result). – heap underrun Nov 18 '21 at 20:23
  • @user4581301 That's pretty sweet - didn't know you could use non-constant values like that. I've updated my answer, please review and let me know if you see any other errors. – LVB Nov 18 '21 at 20:26
  • @heapunderrun, yes - that was a natural extension I picked up on from being able to use non-constant values. Admittedly, I'm not sure I would ever do this (use a static variable as a default parameter value) but it is possible so I've updated my answer. – LVB Nov 18 '21 at 20:29
  • I'll be honest with you: I didn't either until I stopped and looked. I've done it with global variables, but never tried it with a class member. Good edit, but I don't think the pointer's the right way to go. An overload as suggested by Davis Herring is much easier on the programmer: `void print_num(){ print_num(val); }`. A decent compiler will optimize that down to next-to-nothing. – user4581301 Nov 18 '21 at 20:30
  • Side note: The issue is currently covered in the Standard at **[dcl.fct.default]** [point 9](https://eel.is/c++draft/dcl.fct.default#9) and is strongly suggested by point 8's ban on `this`. – user4581301 Nov 18 '21 at 20:34
1

Your function definition doesn't actually know what val is because it doesn't actually live inside of your class. The compiler is actually making a function that contains your class as a parameter and abstracts away all of that. You'll want to set num to val within the function body.