Подтвердить что ты не робот

Почему я не могу неявно построить объект, заданный подходящим конструктором при передаче аргументу?

В приведенном ниже примере почему я не могу просто передать string в printFoo()?

#include <string>
#include <iostream>

using namespace std;

class Foo {
public:
  Foo(const Foo &foo) : str(foo.str) {}
  Foo(string str) : str(str) {}

  string str;
};

void printFoo(Foo foo) {
  cout << foo.str << endl;
}

int main() {
  Foo foo("qux");
  printFoo(foo); // OK

  printFoo("qix"); // error: no matching function for call to 'printFoo'

  return 0;
}

По какой-то причине у меня было в голове, что конструктор будет автоматически определяться и использоваться для построения объекта.

Почему я не могу это сделать, но могу передать константу char[n] аргументу, принимающему std::string, например?

4b9b3361

Ответ 1

Было бы задействовано неявное преобразование:

  • to std::string
  • to Foo

С++ делает не более одного:

От 4 стандартных конверсий (N3337)

Стандартные преобразования - это неявные преобразования со встроенным значением. В пункте 4 перечисляется полный набор таких преобразований. Стандарт последовательность преобразования представляет собой последовательность стандартных преобразований в в следующем порядке:

- Нулевое или одно преобразование из следующего набора: преобразование lvalue-to-rvalue, преобразование матрицы в указатель и преобразование функции в указатель.

- Нулевое или одно преобразование из следующий набор: интегральные акции, продвижение с плавающей запятой, интегральные конверсии, конверсии с плавающей запятой, плавающие интегралы конверсии, преобразования указателей, указатели на преобразования членов и boolean conversion.

- Нулевое или одно квалификационное преобразование.

Также 12.3 Конверсии (N3337)

1 Тип преобразования объектов класса может быть задан конструкторами и с помощью функций преобразования. Эти преобразования называются определяемыми пользователем конверсий и используются для неявных преобразований типов (п. 4), для инициализация (8.5) и для явных преобразований типов (5.4, 5.2.9).

2 Пользовательские преобразования применяются только там, где они недвусмысленны (10,2, 12,3,2). Конверсии подчиняются правилам контроля доступа (раздел 11). Контроль доступа применяется после разрешения двусмысленности (3.4).

[...]

4 Не более одного пользовательского преобразование (конструктор или функция преобразования) неявно применяется до одного значения.

(Акцент мой)

Ответ 2

В соответствии со стандартом С++ §12.3/4 Conversions [class.conv]:

Не более одного пользовательского преобразования (конструктор или преобразование функция) неявно применяется к одному значению.

Таким образом, компилятору не разрешается применять два преобразования в строке. То есть, во-первых, от const char[4] до std::string и, во-вторых, от std::string до Foo.

Для этого вам нужно будет определить дополнительный конструктор:

Foo(char const *str_) : str(str_) {}

Ответ 3

Это потому, что компилятору разрешено рассматривать одно преобразование.

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

printFoo("qix");

// Actually needs.
printFoo(Foo(std::string("qix")));

Если вы измените это, чтобы передать строку, она будет работать.

printFoo(std::string("qix"));

Основная причина всего этого - строковые литералы имеют тип char const[<size>] NOT std::string

Ответ 4

Как уже упоминалось, проблема в том, что необходимы 2 преобразования. Вы можете использовать литерал s для преобразования строкового литерала в фактический std::string

printFoo("qix"s);

DEMO