0

Context:

A function (from some API I cannot modify) returns a constant reference to an object from an objectRegistry:

const Foo& f(args)

I need to obtain the constant Foo object, but I require a different instance of Foo based on some conditional.

Naievely, I'd first declare what f returns, and then call f to initialise the variable foo.

const Foo& foo; // Declare
if( something == true ){
    foo = f(arg1); // Initialise
}else{
    foo = f(arg2); // Initialise
}
// It is guaranteedly initialised in this line

This will not work, as this will first (I assume) call a constructor with empty argument. Afterwards you have a const object that you cannot overwrite. Rather, the compiler immediately complains: error: 'foo' declared as reference but not initialized. The same problem occurs if foo is declared as const Foo foo;

The following does work.

const Foo* fooPtr;
if( something == true ){
    fooPtr = &f(1);
}else{
    fooPtr = &f(2);
}
const Foo& foo = *fooPtr;

Questions:

  • Are there any issues with this method, other than being ugly (imo)?
  • Are there nicer ways to the same end?

Somewhat related topics:

  • 2
    I've seen this problem before. Answer: Ternary conditional. – LogicStuff Aug 15 '17 at 13:32
  • `const Foo foo = *fooPtr;` will create a new object. probably you was trying to write `const Foo & foo = *fooPtr;`? – user7860670 Aug 15 '17 at 13:33
  • @Kevin van As What about the conditional operator? – Vlad from Moscow Aug 15 '17 at 13:33
  • `const Foo& some_name = make_foo(some_parameter);`? – NathanOliver Aug 15 '17 at 13:34
  • 1
    Possible duplicate of [Right way to conditionally initialize a C++ member variable?](https://stackoverflow.com/questions/1014518/right-way-to-conditionally-initialize-a-c-member-variable) – LogicStuff Aug 15 '17 at 13:34
  • @VTT That is certainly what I wanted yes, but no, I did not realise that it'd call the copy constructor. Thank you. I fixed the question. – Kevin van As Aug 15 '17 at 13:44
  • He means something like this: `const Foo& foo = f(something ? arg1 : arg 2);` – xander Aug 15 '17 at 13:45
  • @LogicStuff I'd argue that that question is slightly different, although certainly helpful. (I didn't use the right search terms to find that answer.) The ternary conditional can indeed resolve my initialisation problem, and I did consider it. However, it will become very ugly if I do not merely have one condition, but (say) 5 different cases. That's why I did not go for that option. (Then again, many if/elif's stacked won't be nice either.) – Kevin van As Aug 15 '17 at 13:55
  • If you have many options you can also try a lambda function with a switch-case (or `if`'s depending on your decision type), but to choose what is best we need to see more details in your question. Here my idea `const Foo& foo = [&](){ switch(someInt) { case 1: return Foo("a"); case 2: return Foo("b"); ... ; default return Foo(); } }();` (it looks a little strange at first, but it should work). – xander Aug 15 '17 at 14:06

1 Answers1

2

You could use a wrapper

const Foo& getFooWrapper(something) {  // assume returning a ref is fine,                                         
    if (something) return f(1);        // because f already returns a const&
    else           return f(2);
}

and then simply

const Foo& foo = getFooWrapper();
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • Hm, that should work. Question though, did you mean to write `const Foo& getFooWrapper...` (so with a reference)? Otherwise objects are being copied, do they not? – Kevin van As Aug 15 '17 at 13:59
  • @KevinvanAs no, the function should not return a reference, as the reference would be invalid outside of the scope of the function. You could argue about the `const`, but I would leave it for the sake of documentation, as I understood your question the `const` is essential (i mean otherwise there would be no point in writing this wrapper) – 463035818_is_not_an_ai Aug 15 '17 at 14:01
  • The const is indeed essential, as the function f returns a const. I cannot / am not supposed to const_cast that away. ___ I do not understand why "the value would be invalid outside of the scope of the function". I know that you cannot take the address of a temporary, but if `f` returns a const&, then you may assume that it is safe to have `getFooWrapper` return a const& as well? It would be the very same const&? I don't see where the problem comes in. Would you mind clarifying? – Kevin van As Aug 15 '17 at 14:24
  • @KevinvanAs yes, I overlooked it and didnt see that `f` returns a reference, so it should be fine to return a reference. Concerning the `const` I am not sure if you have a misunderstanding or if I misunderstand you.... You can always copy a `const` object and do whatever you like with that copy, thus for the return type `Foo` or `const Foo` makes no difference (though it does for the reference) – 463035818_is_not_an_ai Aug 15 '17 at 14:29
  • I was aware that you can copy a const object to make a local modifiable version of that object. However, I did not consider that in my previous comment, as Foo is a gigantic scientific computing matrix of typically >1e6 doubles. So my mind is set to make sure I do not accidentally copy the entire matrix. ____ In your previous code, when you returned `const Foo` by value, could you clarify for me if there would be a copy involved? The rule I have in my head is that "any pass by value equals a copy, unless the compiler is smart enough to save me". Would that be correct? – Kevin van As Aug 15 '17 at 14:45
  • 1
    @KevinanAs thats correct, just that return value optimization (RVO) is so common that you can almost be certain that it takes place. Nevertheless, you are completely right: if `f` returns a reference then returning a `Foo` from the wrapper introduces an unnecessary copy – 463035818_is_not_an_ai Aug 15 '17 at 14:54