Почему добавление двух строковых литералов не использует operator + (const string &, const string &)?

Изменить: я переформатировал сообщение, чтобы быть более четким.

Почему это работает:

struct A {};

struct B {
    B(A){}
};

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

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

и это не?

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";
}

По существу, в первом примере a1 и a2 оба преобразуются в объекты B через неявное преобразование и используют operator+(const B&, const B&) для добавления.

Следуя этому примеру, я ожидал бы "Hello" и "world" для преобразования в объекты B снова через неявный конструктор и использовать operator+(const B&, const B&) для добавления друг к другу. Вместо этого возникает ошибка, которая указывает, что строки C-стиля не пытаются преобразовать пользователя в B для добавления. Почему это? Существует ли фундаментальное свойство, предотвращающее это?

Ответ 1

В вашем первом примере разрешению перегрузки разрешено находить ваш operator+:

[C++14: 13.3.1.2/2]: Если любой из операндов имеет тип, который является классом или перечислением, может быть объявлена ​​определяемая пользователем операторная функция, которая реализует этот оператор, или может потребоваться определенное пользователем преобразование преобразуйте операнд в тип, подходящий для встроенного оператора. В этом случае разрешение перегрузки используется для определения того, какая операторная функция или встроенный оператор должны быть вызваны для реализации оператора. [..]

[C++14: 13.3.2/1]: Из набора функций-кандидатов, построенных для данного контекста (13.3.1), выбирается набор жизнеспособных функций, из которых лучшая функция будет выбрана путем сравнения преобразования аргументов последовательности для наилучшего соответствия (13.3.3). Выбор жизнеспособных функций рассматривает отношения между аргументами и функциональными параметрами, отличными от ранжирования последовательностей преобразования.

[C++14: 13.3.2/2]: Во-первых, , чтобы быть жизнеспособной функцией, функция-кандидат должна иметь достаточно параметров для согласования числа с аргументами в списке.

  • Если в списке есть аргументы m, все кандидатские функции, имеющие точно m параметры, жизнеспособны.
  • [..]

[C++14: 13.3.2/3]: Во-вторых, для F для жизнеспособной функции для каждого аргумента будет существовать неявная последовательность преобразований (13.3.3.1), которая преобразует этот аргумент в соответствующий параметр F. [..]

(Вы можете сами изучить формулировку "неявной последовательности преобразований", чтобы увидеть, что вызов operator+ допустим, правила слишком подробные, чтобы гарантировать вербальное воспроизведение здесь.)

Однако во втором примере разрешение перегрузки ограничено базовым механизмом арифметического добавления (который не определен для const char[N] или const char*), фактически запрещающий рассматривать любую функцию operator+:

[C++14: 13.3.1.2/1]: Если никакой операнд оператора в выражении не имеет тип, который является классом или перечислением, оператор считается встроенным оператором и интерпретируется в соответствии с пунктом 5.

[C++14: 5.7/1]: [..] Для добавления оба операнда должны иметь арифметический или неперечисленный тип перечисления, или один операнд должен быть указателем на полностью определенный тип объекта, а другой должен иметь интегральный или неперечисленный тип перечисления. [..]

[C++14: 5.7/3]: Результатом двоичного + оператора является сумма операндов.

Ответ 2

1. Объяснение ошибки компилятора:

Причина того, что вы не можете объединить два строковых литерала с помощью оператора "+",
потому что строковые литералы - это просто массивы символов, и вы не можете объединить два массива.

Массивы будут неявно преобразованы в указатель их первого элемента.

Или, как описывает стандарт:

[conv.array]
Lvalue или rvalue типа "массив N T" или "массив неизвестных границ" of T "можно преобразовать в prvalue типа" указатель на T ". Результат является указателем на первый элемент массива.

Что вы действительно делаете в примере выше,
пытается добавить два указателя const char вместе, и это невозможно.


2. Почему строковые литералы неявно конвертируются:

Поскольку массивы и указатели являются фундаментальными типами, вы не можете предоставить неявный оператор разговора, как вы это делали в примере вашего класса.

Главное иметь в виду, что std::string знает, как взять char[], но char[] не знает, как стать std::string. В вашем примере вы использовали B в качестве замены char[], но вы также дали ему возможность конвертировать себя в A.


3. Альтернативы:

Вы можете объединить строковые литералы, оставив оператор плюс.

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

Необязательно, вы можете сделать "стек" std::string, а затем использовать std::string перегруженный оператор "+":

std::string("stack") + "overflow"; //this will work