auto variable declaration without definition

Viewed 326

I was wondering if there's an elegant solution for the following issue:

Let's say I'd like to have a variable holding a value with a pretty complex type and would the compiler to automatically infer it's type, but declare first and give value later in the code because of initialization in an if statement.

For example:

{
  auto a;
  if (some predicate)
      a = init_a(1);
  else
      a = init_a(2);
}

Obviously this code doesn't compile, but the I think that the compiler has all the information it needs to infer the type.

If 'a' was just an int, this was not a problem, but if the type is a complex template, I don't want to have to write it (or in some cases even know it). Also, i wouldn't want to call the default constructor of a and then write over it.

Possible solutions:

  1. Template the type of 'a'.
  2. initialize 'a' using a lambda and move the predicate into the lambda.
  3. Just write the type of 'a' instead auto.
  4. Use a void pointer/shared_ptr and then init 'a' on the heap.

Each of these has its own drawbacks.

Is there a more elegant solution for it?

3 Answers

The compiler doesn't have infinite lookahead what is happening further on in the code. It only knows what's happening at the current statement. Therefore it can't deduce any types without an initializer.

If you don't know the return-type of init_a then you could use decltype:

decltype(init_a(1)) a;

You can also use a lambda call to initialize the variable:

auto a = [ /* Captures needed for the condition... */ ]()
{
    if (some_condition)
        return init_a(1);
    else
        return init_a(2);
}();  // Call the lambda immediately

Or, as mentioned in many comments, use the ternary conditional expression:

auto a = some_condition ? init_a(1) : init_a(2);

There's a technique called "Immediately Invoked Lambda Expression" that is using lambda to initialize a variable in a complex way. Using this approach your a can be const which improves const-correctness. More details here.

For a simple binary predicate, consider just using the ternary operator:

struct A { int a; };

A initA(int a) { return A{a}; }
bool somePredicate(int input) { return input == 42; }

int main() {
    const auto input = 42;
    const auto a = somePredicate(input) ? initA(1) : initA(2);
}

for more complex initialization logic (beyond a binary case), wrap the initialization logic in a lambda:

struct A { int a; };

A initA(int a) { return A{a}; }
bool somePredicate(int input) { return input == 42; }

int main() {
    const auto input = 42;
    const auto a = []() { 
        if (somePredicate(input)) { return initA(1); }
        else if (input == 100) { return initA(100); }
        else { return initA(2); }}();
}

Both these approaches come with additional possibility of making the variable to be initialized const.

Related