15

I know it's perfectly possible to initialise a char array with a string literal:

char arr[] = "foo";

C++11 8.5.2/1 says so:

A char array (whether plain char, signed char, or unsigned char), char16_t array, char32_t array, or wchar_t array can be initialized by a narrow character literal, char16_t string literal, char32_t string literal, or wide string literal, respectively, or by an appropriately-typed string literal enclosed in braces. Successive characters of the value of the string literal initialize the elements of the array. ...

However, can you do the same with two string literals in a conditional expression? For example like this:

char arr[] = MY_BOOLEAN_MACRO() ? "foo" : "bar";

(Where MY_BOOLEAN_MACRO() expands to a 1 or 0).

The relevant parts of C++11 5.16 (Conditional operator) are as follows:

1 ... The first expression is contextually converted to bool (Clause 4). It is evaluated and if it is true, the result of the conditional expression is the value of the second expression, otherwise that of the third expression. ...

4 If the second and third operands are glvalues of the same value category and have the same type, the result is of that type and value category and it is a bit-field if the second or the third operand is a bit-field, or if both are bit-fields.

Notice that the literals are of the same length and thus they're both lvalues of type const char[4].

GCC one ideone accepts the construct. But from reading the standard, I am simply not sure whether it's legal or not. Does anyone have better insight?

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455

2 Answers2

10

On the other hand clang does not accept such code (see it live) and I believe clang is correct on this (MSVC also rejects this code ).

A string literal is defined by the grammar in section 2.14.5:

string-literal:
    encoding-prefixopt" s-char-sequenceopt"
    encoding-prefixoptR raw-string

and the first paragraph from this section says (emphasis mine):

A string literal is a sequence of characters (as defined in 2.14.3) surrounded by double quotes, optionally prefixed by R, u8, u8R, u, uR, U, UR, L, or LR, as in "...", R"(...)", u8"...", u8R"(...)", u"...", uR"˜(...)˜", U"...", UR"zzz(...)zzz", L"...", or LR"(...)", respectively

and it further says that the type of a narrow string literal is:

“array of n const char”,

as well as:

has static storage duration

but an “array of n const char”, with static storage duration is not a string literal since it does not fit the grammar nor does it fit paragraph 1.

We can make this fail on gcc if we use a non-constant expression (see it live):

bool x = true ;
char arr[] = x ? "foo" : "bar";

which means it is probably an extension, but it is non-conforming since it does not produce a warning in strict conformance mode i.e. using -std=c++11 -pedantic. From section 1.4 [intro.compliance]:

[...]Implementations are required to diagnose programs that use such extensions that are ill-formed according to this International Standard. Having done so, however, they can compile and execute such programs.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • 3
    Agree, 8.5.2/1 seems pretty clear that it is *string literal* and not *some expression which evaluates to a string literal*. (even if you consider the ternary operator as evaluating to a string literal in this case). – M.M Jan 23 '15 at 20:50
3

This works in GCC in C++11 or newer because the literals you're providing are deterministic during compile time (eg, they are constexpr). Since the compiler can figure out which one is true, it is allowed to figure out which one to use.

To remove the constexpr ability, try something like this:

#include <iostream>
#include <cstdlib>

int main() {
    bool _bool = rand();
    char arr[] = (_bool) ? "asdf" : "ffff";

    std::cout << arr << std::endl;
}

GCC then errors out with:

g++ test.cpp -std=c++11
test.cpp: In function ‘int main()’:
test.cpp:6:34: error: initializer fails to determine size of ‘arr’
  char arr[] = (_bool) ? "asdf" : "ffff";
                                  ^
test.cpp:6:34: error: array must be initialized with a brace-enclosed initializer

I don't know the standard's text definition well enough to know where or why this is valid, but I feel that it is valid.

For further reading on constexpr and how it can impact compilability, see the answer by @ShafikYaghmour in another question.

Community
  • 1
  • 1
inetknght
  • 4,300
  • 1
  • 26
  • 52
  • 1
    Note that *constant expression* and *constexpr* are related but not the same, I cover that to some degree [here](http://stackoverflow.com/a/26025026/1708801) so `gcc` is treating it as a constant expression. – Shafik Yaghmour Jan 23 '15 at 20:20
  • True, I can see that there's a distinction. But it's a fine point and I don't think that the difference applies in c++11 and beyond... unless you can show that it does? – inetknght Jan 23 '15 at 20:24
  • 1
    It is purely informative, hopefully you find it helpful. I would just note that since the question is tagged language-lawyer people will tend to be more picky about details. – Shafik Yaghmour Jan 23 '15 at 20:29