10

Edit: I have reformatted the post to be clearer.

Why does this work:

struct A {};

struct B {
    B(A){}
};

void operator+(const B&, const B&) {}

int main()
{
    A a1, a2;
    a1 + a2;
}

and this does not?

struct B {
    B(const char*){}
};

void operator+(const B&, const B&) {} //error: invalid operands of types 'const char [6]' and 'const char [6]' to binary 'operator+'|

int main()
{
    "Hello" + "world";
}

Essentially, in the first example a1 and a2 both convert to B objects through the implicit conversion and use the operator+(const B&, const B&) to add.

Following from this example, I would have expected "Hello" and "world" to convert to B objects, again through the implicit constructor, and use operator+(const B&, const B&) to add to each other. Instead there is an error, which indicates the C-style strings do not attempt a user-defined conversion to B in order to add. Why is this? Is there a fundamental property that prevents this?

Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
Silversonic
  • 1,289
  • 2
  • 11
  • 26
  • 1
    You didn't involve `std::string` with `"stack" + "overflow";`. – πάντα ῥεῖ Oct 18 '15 at 19:45
  • 1
    @πάνταῥεῖ He is aware of that, the question is why the implicit conversion to `std::string` does not fix that for him. – Baum mit Augen Oct 18 '15 at 19:59
  • 1
    Your minimal example is a little bit off as `const char*` has no conversion operator, but the conversion happens via `std::string`s constructor. [This](http://coliru.stacked-crooked.com/a/1c4aafeb95886b92) would be a little more apt. – Baum mit Augen Oct 18 '15 at 20:06
  • 1
    https://stackoverflow.com/questions/6677072/overload-resolution-failure-when-streaming-object-via-implicit-conversion-to-str – Jonathan Potter Oct 18 '15 at 20:07
  • Oops, that dupe does not explain it. Sorry. http://coliru.stacked-crooked.com/a/0c8bc9e13e198e7b – Baum mit Augen Oct 18 '15 at 20:56
  • @BaummitAugen It looks like it could explain my second query (why `c` is not converted to `string` - this caused template issues with my compiler) but I'm not sure. I'm still on the verge of learning C++ and I'm not familiar with templates yet - but am familiar with user-defined conversions. – Silversonic Oct 18 '15 at 21:20
  • Also I've changed my second example to match yours. – Silversonic Oct 18 '15 at 21:32
  • @BaummitAugen Is it wrong to completely edit my post despite receiving answers? I want to add your example http://coliru.stacked-crooked.com/a/0c8bc9e13e198e7b, which illustrates the crux of the problem and shows it isn't to do with `std::string` or `templates` but rather how user-defined conversions are treated. – Silversonic Oct 18 '15 at 21:52
  • @Silversonic That is completely fine as it does not invalidate any correct answers, but adds useful research. Well, maybe better leave the string part in as motivation. – Baum mit Augen Oct 18 '15 at 21:54

2 Answers2

5

In your first example, overload resolution is allowed to find your operator+:

[C++14: 13.3.1.2/2]: If either operand has a type that is a class or an enumeration, a user-defined operator function might be declared that implements this operator or a user-defined conversion can be necessary to convert the operand to a type that is appropriate for a built-in operator. In this case, overload resolution is used to determine which operator function or built-in operator is to be invoked to implement the operator. [..]

[C++14: 13.3.2/1]: From the set of candidate functions constructed for a given context (13.3.1), a set of viable functions is chosen, from which the best function will be selected by comparing argument conversion sequences for the best fit (13.3.3). The selection of viable functions considers relationships between arguments and function parameters other than the ranking of conversion sequences.

[C++14: 13.3.2/2]: First, to be a viable function, a candidate function shall have enough parameters to agree in number with the arguments in the list.

  • If there are m arguments in the list, all candidate functions having exactly m parameters are viable.
  • [..]

[C++14: 13.3.2/3]: Second, for F to be a viable function, there shall exist for each argument an implicit conversion sequence (13.3.3.1) that converts that argument to the corresponding parameter of F. [..]

(You may examine the wording for "implicit conversion sequence" yourself to see that the operator+ call is permissible; the rules are too verbose to warrant verbatim reproduction here.)

However, in your second example, overload resolution is constrained to a basic arithmetic addition mechanism (one which is not defined for const char[N] or const char*), effectively prohibiting any operator+ function from being considered:

[C++14: 13.3.1.2/1]: If no operand of an operator in an expression has a type that is a class or an enumeration, the operator is assumed to be a built-in operator and interpreted according to Clause 5.

[C++14: 5.7/1]: [..] For addition, either both operands shall have arithmetic or unscoped enumeration type, or one operand shall be a pointer to a completely-defined object type and the other shall have integral or unscoped enumeration type. [..]

[C++14: 5.7/3]: The result of the binary + operator is the sum of the operands.

Community
  • 1
  • 1
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
2

1. Explaining your compiler error:

The reason you can't concatenate two string literals using the '+' operator,
is because string literals are simply arrays of characters, and you can't concatenate two arrays.

Arrays will be implicitly converted to the pointer of their first element.

Or as the standard describes it:

[conv.array]
An lvalue or rvalue of type “array of N T” or “array of unknown bound of T” can be converted to a prvalue of type “pointer to T”. The result is a pointer to the first element of the array.

What you are really doing in the example above,
is trying to add two const char pointers together, and that is not possible.


2. Why the string literals aren't implicitly converted:

Since arrays and pointers are fundamental types, you can't provide an implicit conversation operator as you have done in your class example.

The main thing to keep in mind, is that std::string knows how to take in char[], but char[] does not know how to become a std::string. In your example, you've used B, as a replacement to char[], but you've also given it the ability to convert itself to A.


3. Alternatives:

You can concatenate string literals by leaving out the plus operator.

"stack" "overflow"; //this will work as you indented

Optionally, you could make "stack" a std::string, and then use the std::string's overloaded '+' operator:

std::string("stack") + "overflow"; //this will work
Trevor Hickey
  • 36,288
  • 32
  • 162
  • 271
  • 3
    This does not explain why the implicit conversion to `std::string` does not happen. – Baum mit Augen Oct 18 '15 at 19:53
  • 2
    As @BaummitAugen mentions, this answer doesn't explain why both `char*` operands couldn't be converted to `string` by user-defined conversions and `operator+(const string&, const string&)` be used. – Silversonic Oct 18 '15 at 20:03
  • Can you cite the relevant section of the C++ standard? Because there's an answer that does and it's completely different (based on string being a template). – David Schwartz Oct 18 '15 at 20:21
  • @BaummitAugen I was addressing their statement: "I would expect a conversion of both C-style strings to a string", which I believe is the crux of their question. My answer fits what the title question is asking. It's true that they go on to emulate their desired behavior using non-fundamental types, and then reach an unrelated issue similar to the one marked as a duplicate. But the problem they ran into while experimenting, is not the same as the question they are asking. – Trevor Hickey Oct 18 '15 at 20:39
  • *"I was addressing their statement: "I would expect a conversion of both C-style strings to a string""* You are only addressing this is the the second part of your answer, and that part is wrong. – Baum mit Augen Oct 18 '15 at 20:45
  • You claim that the problem is that `const char[]` has no conversion operator, but that is not the case. http://coliru.stacked-crooked.com/a/1c4aafeb95886b92 – Baum mit Augen Oct 18 '15 at 21:03