"" + something in C++

  • "" is a string literal. Those have the type array of N const char. This particular string literal is an array of 1 const char, the one element being the null terminator.

  • Arrays easily decay into pointers to their first element, e.g. in expressions where a pointer is required.

  • lhs + rhs is not defined for arrays as lhs and integers as rhs. But it is defined for pointers as the lhs and integers as the rhs, with the usual pointer arithmetic.

  • char is an integral data type in (i.e., treated as an integer by) the C++ core language.

==> string literal + character therefore is interpreted as pointer + integer.

The expression "" + c is roughly equivalent to:

static char const lit[1] = {'\0'};
char const* p = &lit[0];
p + c // "" + c is roughly equivalent to this expression

You return a std::string. The expression "" + c yields a pointer to const char. The constructor of std::string that expects a const char* expects it to be a pointer to a null-terminated character array.

If c != 0, then the expression "" + c leads to Undefined Behaviour:

  • For c > 1, the pointer arithmetic produces Undefined Behaviour. Pointer arithmetic is only defined on arrays, and if the result is an element of the same array.

  • If char is signed, then c < 0 produces Undefined Behaviour for the same reason.

  • For c == 1, the pointer arithmetic does not produce Undefined Behaviour. That's a special case; pointing to one element past the last element of an array is allowed (it is not allowed to use what it points to, though). It still leads to Undefined Behaviour since the std::string constructor called here requires its argument to be a pointer to a valid array (and a null-terminated string). The one-past-the-last element is not part of the array itself. Violating this requirement also leads to UB.


What probably now happens is that the constructor of std::string tries to determine the size of the null-terminated string you passed it, by searching the (first) character in the array that is equal to '\0':

string(char const* p)
{
    // simplified
    char const* end = p;
    while(*end != '\0') ++end;
    //...
}

this will either produce an access violation, or the string it creates contains "garbage". It is also possible that the compiler assumes this Undefined Behaviour will never happen, and does some funny optimizations that will result in weird behaviour.


By the way, clang++3.5 emits a nice warning for this snippet:

warning: adding 'char' to a string does not append to the string [-Wstring-plus-int]

return "" + c; // Here
       ~~~^~~

note: use array indexing to silence this warning


There are a lot of explanations of how the compiler interprets this code, but what you probably wanted to know is what you did wrong.

You appear to be expecting the + behavior from std::string. The problem is that neither of the operands actually is a std::string. C++ looks at the types of the operands, not the final type of the expression (here the return type, std::string) to resolve overloading. It won't pick std::string's version of + if it doesn't see a std::string.

If you have special behavior for an operator (either you wrote it, or got a library that provides it), that behavior only applies when at least one of the operands has class type (or reference to class type, and user-defined enumerations count too).

If you wrote

std::string("") + c

or

std::string() + c

or

""s + c // requires C++14

then you would get the std::string behavior of operator +.

(Note that none of these are actually good solutions, because they all make short-lived std::string instances that can be avoided with std::string(1, c))

The same thing goes for functions. Here's an example:

std::complex<double> ipi = std::log(-1.0);

You'll get a runtime error, instead of the expected imaginary number. That's because the compiler has no clue that it should be using the complex logarithm here. Overloading looks only at the arguments, and the argument is a real number (type double, actually).

Operator overloads ARE functions and obey the same rules.