В C++ нет преобразования const char [n] в const char *?

Код:

#include <iostream>
#include <cstring>

class MyString
{
  public:
  MyString() {
    std::cout << "1";
  }
  explicit MyString(const char *str) {
    std::cout << "2";
  }
};

int main()
{
  MyString str1 = MyString("abcde");
  MyString str2("abcde");
  MyString str3 = "abcde";

  return 0;
}

str1 и str2 создаются нормально, распечатываются 2. Но когда я запускаю его с помощью str3, я получаю следующее сообщение об ошибке:

Compilation failed due to following error(s).main.cpp: In function ‘int main()’:
main.cpp:19:19: error: conversion from ‘const char [6]’ to non-scalar type ‘MyString’ requested
19 |   MyString str3 = "abcde";

Разве все три строки не должны вызывать второй конструктор из-за RVO? Или это потому, что я использую компилятор, отличный от RVO? Я попробовал это как на CMake, так и на gcc, и оба получили одно и то же сообщение об ошибке.

Я попытался создать operator= и все равно получил ту же ошибку, так что это явно не от RVO:

#include <iostream>
#include <cstring>

class MyString
{
  public:
  MyString() {
    std::cout << "1";
  }
  explicit MyString(const char *str) {
    std::cout << "2";
  }
  MyString& operator=(const char *str) {
    std::cout << "3";
    return *this;
  }
};

int main()
{
  MyString str1 = MyString("abcde");
  MyString str2("abcde");
  MyString str3 = "abcde";

  return 0;
}

Я довольно долго искал, но не смог найти ответа.

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

BoP 09.06.2024 11:40

Такие оптимизации, как RVO, никогда не играют роли в принятии решения о том, корректна ли программа или нет, и оператор присваивания здесь совершенно не важен.

Weijun Zhou 09.06.2024 11:41

Кроме того, несмотря на использование символа =, здесь нет присваивания, только инициализация. Поэтому оператор присваивания не рассматривается.

BoP 09.06.2024 11:45

На YouTube есть пара очень хороших видеороликов, объясняющих кошмар инициализации на C++. Мой любимый — C++ on Sea 2019 – Тимур Думлер – «Инициализация в современном C++» , но другие почти так же хороши: CppCon 2018 – Николай Йосуттис – «Кошмар инициализации в C++».

tbxfreeware 09.06.2024 14:08

@WeijunZhou, насколько я знаю, RVO используется для оптимизации таких вещей, как вместо вызова конструктора и создания объекта, а затем копирования его с помощью op=, он просто вызывает конструктор и создает объект do, без какого-либо копирования при написании таких вещей как 'A a = A(5)'. Я подумал, что в конкретном компиляторе нет RVO, поэтому попробовал его просто для проверки, но, как я уже писал, проблема была не в этом. Но я думаю, что это всего лишь один случай компилятора, который, как я понял, во многом зависит от компилятора.

Average_C_Enjoyer 09.06.2024 18:53

@tbxfreeware Я проверю их, поскольку сейчас изучаю эту тему и знаю, что большинство вещей зависят от компилятора, но большое спасибо за это!

Average_C_Enjoyer 09.06.2024 18:54

@BoP, спасибо, это решает проблему! Я рассмотрю подробнее явный вариант, поскольку знал только, что его можно использовать, поэтому нельзя вызывать функции, которые принимают объект с чем-то из однопараметрического конструктора, но я определенно не знал, что это также один из их вариантов использования. (или, я думаю, случаи антииспользования). Большое спасибо!

Average_C_Enjoyer 09.06.2024 18:56

Я имел в виду, что оптимизация применяется на более позднем этапе и представляет собой детали реализации. Даже если компилятор решит пропустить вызов конструктора в рамках оптимизации, сам вызов конструктора все равно должен быть правильно сформирован, поэтому вам вообще не следует рассматривать оптимизацию, если вы хотите узнать, правильно ли сформирована какая-либо программа на C++. . И нет, РВО не имеет отношения к operator=. Возможно, вы думаете о конструкторе копирования. Возможно, это как-то связано с RVO. Но оператор присваивания никогда не имеет значения. A a=A(5) не вызывает оператор присваивания, даже если есть =.

Weijun Zhou 10.06.2024 04:42

«копирование с помощью op = ", похоже, вас запутало. copying и op= на самом деле являются взаимоисключающими. Если вы копируете объект, op= не имеет значения. op= актуален только в том случае, если у вас уже есть полный объект и вы его перезаписываете, чего не происходит в RVO. В частности, A a=f() не создает A, а затем вызывает op= с помощью f(). Он копирует a из f(), что включает в себя конструктор копирования, а не op=, и этот вызов конструктора копирования может быть опущен RVO.

Weijun Zhou 10.06.2024 04:52
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
9
117
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

MyString str3 = "abcde"; выполняет инициализацию копирования, которая не работает с явными конструкторами. Более формально:

[...] если T является типом класса, а cv-неквалифицированная версия типа другого не является T и не является производной от T, или если T не является типом класса, но тип other является типом класса, определяемые пользователем последовательности преобразования, которые могут конвертировать из типа другого в T (или в тип, производный от T, если T является типом класса и доступна функция преобразования), проверяются, и лучшая из них выбирается посредством разрешения перегрузки.

Здесь T — это тип класса (MyString), а other не принадлежит к типу класса (const char[6]), а определяемая пользователем последовательность преобразований отсутствует, поскольку она не может содержать конструкторы explicit.

Другие вопросы по теме