60

I want to make an argument for one of the member functions optional. When no argument is provided, it would use an member variable.

However, when I tried to compile it it shows

error: invalid use of non-static data member 'Object::initPos'

Just to isolate the problem, I tried defaulting an int type and it compiled fine. I wonder what is the problem with my code and how I could use a member function as default value.

Thank you for your help!

Object.h

class Object
{
    public:
       ...
       void MoveTo(double speed, Point position);

    protected:
       Point initPos; 
       Point currPos;

};

Object.c

void Object::MoveTo(double speed, Point position = initPos)
{
    currPos = postion;
}

Point.h

class Point
{
    ...

    private:
       double x;
       double y;
       double z; 
};
Jason
  • 36,170
  • 5
  • 26
  • 60
tuzzer
  • 1,119
  • 3
  • 12
  • 20
  • 1
    Your question made me spawn this one: http://stackoverflow.com/questions/9286801/valid-expressions-for-default-function-arguments – Emile Cormier Feb 15 '12 at 01:42

4 Answers4

75

Default argument expressions for a member function can only depend on things that are in class or global scope. The default argument also has to be specified in the method's declaration (i.e. in the header file).

To get around this, you need 2 overloads of your MoveTo method. One that takes 1 argument, and another that takes 2 arguments. The method taking 1 argument calls the other method, passing along the value that you consider as the default.

void Object::MoveTo(double speed)
{
    MoveTo(speed, initPos);
}

void Object::MoveTo(double speed, Point position)
{
    // Everything is done here.
}

Note that when you make MoveTo(double) call MoveTo(double, Point), it allows you to write the implementation of MoveTo only once, thereby respecting the DRY principle.

Emile Cormier
  • 28,391
  • 15
  • 94
  • 122
  • 2
    Thanks! I do know that I can do it this way. I was just wondering if there's a shorter way to do it, such as making it a default argument. But I guess this would be the only way to do it. – tuzzer Feb 15 '12 at 01:14
  • Then what if, instead of just one optional argument, I have many of them? say void Object::MoveTo(double speed, Point position = initPos, Point a = m_a, Point b = m_b, Point c = m_c) then won't I have to make many functions??? – tuzzer Feb 15 '12 at 01:17
  • 1
    @MatthewChan: Unfortunately, yes. Instead of passing many arguments, perhaps you can change the design so that the user passes an object. This object would already contain reasonable defaults, and the user only changes the ones that need to be different. If you decide to do it this way, you might be interested in the Method Chaining idiom to make setting multiple parameters more concise (http://en.wikipedia.org/wiki/Method_chaining). – Emile Cormier Feb 15 '12 at 01:27
  • 1
    Just a question about Method Chaining then... If in C++, then you would do "return *this" right? Then won't that simply be making a copy of the instance of the object?? Then that instance won't actually change right? Like if I have a object called Car, and I have have an instance of Car called myCar. myCar.setColour(RED).setBrand(PORCHE).setYear(2012) – tuzzer Feb 15 '12 at 02:46
  • `*this` is a *reference* to the current instance. If your setter signature is something like `Car& setColor(Color c)` (note the ampersand), then it will return a reference to the car, and not a copy. On the other hand, if the signature is `Car setColor(Color c)` (note the lack of an ampersand), then a copy of the car would be returned. For the method chaining thing to work, you therefore need to return a reference to the object. – Emile Cormier Feb 15 '12 at 02:53
  • More method chaining info here: http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.20. They call it Named Parameter idiom. – Emile Cormier Feb 15 '12 at 02:57
19

Default values are not part of the prototype, i.e. they're resolved by the caller, not by the function itself. So firstly, they have to be visible to the caller. Secondly, they cannot access protected members of the class. (I'm pretty sure you can't even use public members as defaults, but I'm too tired to check.)

To solve the problem, use chained overloads as suggested in other answers.

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • Thanks. I tried changing initPos to public, but as you said, it still won't work. So is it that we can only use constant as default argument (like not member function, something that you can type out)?? – tuzzer Feb 15 '12 at 01:10
  • 4
    ```they're resolved by the caller, not by the function itself``` : I see, that's the reason. – Arkajyoti Banerjee Nov 30 '19 at 19:55
  • 1
    This is a better answer than the accepted answer because you actually explained the problem. – VHS Jun 18 '20 at 21:41
  • 2
    @Oliver Then why static data members are allowed as defaults? They are not any more visible than other data members (cause the object will exist when calling the member function). Or what about if static member is private? – User 10482 Jul 05 '20 at 20:08
5

You can overload your function member like that:

void Object::MoveTo(double speed, Point position) {
   ....
}

void Object::MoveTo(double speed) {
   Point position = this->initPos;

   MoveTo(speed, position);
}
damson
  • 2,635
  • 3
  • 21
  • 29
  • 1
    unfortunately the only work around that could get the work done, looks ugly though :( – Amr Saber Dec 08 '17 at 22:30
  • @Argento What's ugly about it? It's quite common to have overloads calling other overloads. – Ted Lyngmo Jul 17 '20 at 07:46
  • 1
    @TedLyngmo I am used to other programming languages, like (JS, Python, Kotlin, etc...) compared to them, and considering that C++ does already have syntax for function's default value, this is an ugly workaround and not something that is supported by default like other languages. Nevertheless, it's a matter of personal opinion after all. – Amr Saber Jul 18 '20 at 17:15
  • @Argento Do the languages you mention accept defaults via an implicit `*this` (or the language equivalent)? – Ted Lyngmo Jul 18 '20 at 23:56
  • @TedLyngmo In Kotlin and JS Yes they do, not sure about python haven't used its OOP heavily before. so the mentioned example will become something like `MoveTo(speed, position = initPos) { ... }` – Amr Saber Jul 19 '20 at 17:21
1

Besides overloading, another approach is passing an invalid value as the default value. Then check it inside your function.

void Object::MoveTo(double speed, Point position = InvalidPosition) {
   position = (position != InvalidPosition) ? position : initPos;
   ...
}
DAG
  • 417
  • 4
  • 7