Почему я не могу неявно построить объект, заданный подходящим конструктором при передаче аргументу?
В приведенном ниже примере почему я не могу просто передать 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
, например?
Ответы
Ответ 1
Было бы задействовано неявное преобразование:
С++ делает не более одного:
От 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