Lambda: Is capture of const char * from function scope in lambda undefined behaviour

1k views Asked by At

I have a lambda which uses a const char * defined in function scope. The lambda is executed after the function returns.

void function() {
  const char * UNLOCK_RESULT = "unlockResult";
  .. 
  ..
 TestStep::verifyDictionary(test, [UNLOCK_RESULT](TestStep::Dictionary& dict){
    return 1 == dict[UNLOCK_RESULT].toInt();
  });
  ...
}

If I look at the assembly that GCC generates, the actual char array holding the "unlockResult" is statically allocated. But I was wondering if this is guaranteed by the standard as in theory the const char * is of function scope. Is the capture of const char * undefined behaviour or is this allowed by the standard because of some exception around C++ handling of const char * strings.

I know I can change:

             const char * UNLOCK_RESULT="" 
to 
   constexpr const char * UNLOCK_RESULT=""

And then the discussion falls away because I don't even need to capture it, but I am curious about the const char * case.

2

There are 2 answers

0
Slava On BEST ANSWER

You are mixing 2 concepts. In function scope is variable UNLOCK_RESULT itself, so if you try to use reference or pointer to the variable then you would get UB. But as you use a value of that variable which is pointer to static data you are totally fine as such pointer is not invalidated after function terminates.

If your question is if a pointer to string literal is valid after function terminates, then yes it is valid and guaranteed by C++ standard to have static duration:

Evaluating a string-literal results in a string literal object with static storage duration, initialized from the given characters as specified above. Whether all string literals are distinct (that is, are stored in nonoverlapping objects) and whether successive evaluations of a string-literal yield the same or a different object is unspecified.

(emphasis is mine)

Btw it is an old habit to use identifiers for constants with all uppercase as they used to be defined in preprocessor. When you have compile time identifiers for constants uppercase you would hit exactly the same problem this habit tried to minimize.

0
rustyx On

The behavior is well-defined.

The lambda [UNLOCK_RESULT](TestStep::Dictionary& dict){...} captures the pointer by-value. So UNLOCK_RESULT inside the lambda is a copy of the original UNLOCK_RESULT, the latter does not have to exist for the duration of the lambda.

Now the string literal "unlockResult" has static storage duration, which means it's allocated at program start and remains available for the duration of the program:

All variables which do not have dynamic storage duration, do not have thread storage duration, and are not local have static storage duration. The storage for these entities shall last for the duration of the program.