0

I am spoiling the comma operator in the constructor, so that I can preprocess the parameters and use the processed parameter for initialization.

Say I have the following base and derived classes:

class Base
{
    protected:
        int i;
        int j;
    public:
        Base(int a):i(a),j(a){}
        Base(int a, int b):i(a),j(b){}
};

class Derived:public Base
{
    private:
        int d;
        void inc(int & a) {a++;}
        void inc(int & a, int & b) {a++; b++;}
    public:
        Derived(int a, int b, int c); 
};

I know I can use comma operator to process a parameter and use it to initialize the base part as follows:

Derived::Derived(int a, int b, int c):Base((inc(a),a)),d(c){}

Now what about if I want to pre-process two parameters and use them to initialize the base? I did the following:

Derived::Derived(int a, int b, int c):Base((inc(a,b),(a,b))),d(c){}

But this is not what I want, because the single-parameter base constructor will still be used (since (a,b) is also a comma operator that returns b). Is there anyway to achieve what I want if comma operator is not possible?

Hailiang Zhang
  • 17,604
  • 23
  • 71
  • 117
  • Also, any reason you can't just `Base(inc(a), inc(b))`? – DanielKO Oct 16 '13 at 06:41
  • @DanielKO In my actual programs, a much more complicated pre-processing was used instead of "inc". – Hailiang Zhang Oct 16 '13 at 06:45
  • @DanielKO If not recommended, could you post the reason as an answer? – Hailiang Zhang Oct 16 '13 at 06:45
  • 2
    `Derived(int a, int b, int c):Base((inc(a,b),a),b),d(c){}` – Terenty Rezman Oct 16 '13 at 06:47
  • It's better to make `inc` functions `static` so that you will not invoke member functions of not fully constructed object. – Terenty Rezman Oct 16 '13 at 06:55
  • @TerentyRezman: how can you guarantee that the first argument to `Base()` was evaluated (thus `b` was incremented) before the second argument (`b`)? The language doesn't guarantee it. See [this question](http://stackoverflow.com/questions/2934904/order-of-evaluation-in-c-function-parameters). – DanielKO Oct 16 '13 at 07:00
  • @DanielKO That's right. `b` was not processed when used to initialize the base class. – Hailiang Zhang Oct 16 '13 at 07:04
  • @DanielKO Because the first argument is inside parentheses – Terenty Rezman Oct 16 '13 at 07:08
  • @TerentyRezman: you essentially wrote `func( change(b), b)`, there's no guarantee `change(b)` will be evaluated before `b`. The comma operator introduces a sequence point inside the comma expression, but that comma preceding `b` is not a comma operator, it's just separating the function arguments. – DanielKO Oct 16 '13 at 07:13
  • @DanielKO I'm confused. I believe I wrote `func( (change(b)), b)`. And in this case I assume change(b) will be called first. Are you sure that what I wrote is like `func( change(b), b)` and not like `func( (change(b)), b)`? I don't get it. Anyways your answer to this question seems reasonable at least because of this misunderstanding =) – Terenty Rezman Oct 16 '13 at 07:30
  • 1
    @TerentyRezman redundant parenthesis don't change the fact that the order of evaluation is undefined. You still don't know whether `(change(b))` or `b` will be evaluated first. Same thing if you write `func( ((change(b))), b )`, or `func( (((change(b)))), b )`. – DanielKO Oct 16 '13 at 07:39
  • @DanielKO well that's quite surprising considering that parentheses are used to specify an order. Many thanks for clarifying this! – Terenty Rezman Oct 16 '13 at 07:56
  • @TerentyRezman The parentheses are being used in this case to change the parsing, so the comma is interpreted as an operator instead of an argument separator. If you write `x = a, b;`, that's a comma operator, ensuring you first evaluate `x=a`, then evaluate `b` (an throw the result away). If you write `func(x=a, b)`, that comma is not an operator, it's separating 2 arguments. But if you write `func( (x=a, b) )`, the parenthesized expression is evaluated (`x=a, then `b`, the whole expression has the value of `b`), then `func` is called with the value of `b` as a single argument. – DanielKO Oct 16 '13 at 08:16

1 Answers1

4

Although this can constitute as a mere opinion, I would recommend against writing such expressions; you are putting an extra burden on the maintainer to figure out what exactly is being executed before the actual function is called; even figuring out the number of arguments the function takes will require some effort, because commas would normally be used in that context only to separate arguments.

For a single argument, I would do it like this:

class Derived : Base {
    static int computeSomething(int a) { return a+1; }

    Derived(int a) :
        Base(computeSomething(a)), ...
    { ... }
};

Note I'm returning a new value. It could even take and return by reference if the type of a is too expensive to copy.

For multiple arguments, all needed to be updated at once, I would change the base to receive the entire pack of arguments with a named entity, or maybe a std::tuple, and do it like the single-argument version.

DanielKO
  • 4,422
  • 19
  • 29