Почему оператор преобразования не вызывается при использовании синтаксиса инициализации, и почему сообщение об ошибке clang кажется неправильным?
У меня есть следующий код, который создает один объект t2 с помощью явного конструктора преобразования, который выполняет неявное преобразование t1. Это ожидается и описано в языке программирования C++, в разделе 11.4.1 третьего издания.
#include <iostream>
#include <string>
using namespace std;
class test1 {
public:
test1() {}
operator string() {
cout << "test1 string conversion operator called" << endl;
return string();
}
};
class test2 {
public:
test2() {}
test2(string s) {
cout << "test2 string conversion constructor called" << endl;
}
};
int main() {
test1 t1;
test2 t2(t1);
return 0;
}
И, как и следовало ожидать:
> clang++ --version
Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn)
Target: x86_64-apple-darwin13.0.2
Thread model: posix
> clang++ -std=c++11 test.cc
> ./a.out
test1 string conversion operator called
test2 string conversion constructor called
Однако при изменении конструкции t2 на синтаксис инициализации:
test1 t1;
test2 t2 = t1;
return 0;
Clang выводит следующее:
test.cc:23:15: error: no viable conversion from 'test1' to 'test2'
test2 t2 = t1;
^ ~~
test.cc:13:11: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'test1' to 'const test2 &' for 1st argument
class test2 {
^
test.cc:13:11: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'test1' to 'test2 &&' for 1st argument
class test2 {
^
test.cc:16:9: note: candidate constructor not viable: no known conversion from 'test1' to 'string' (aka 'basic_string<char, char_traits<char>, allocator<char> >') for 1st argument
test2(string s) {
^
test.cc:8:9: note: candidate function
operator string() {
^
1 error generated.
Я не знаю, должна ли инициализация выполнять неявное преобразование, подобное этому, но сообщение об ошибке кажется очень-очень неправильным. неизвестное преобразование из 'test1' в 'string', но в нем даже показан оператор-кандидат функции string() {
Что дает? И что стандарт C++ говорит о неявных преобразованиях в конструкторах инициализации? Я предполагаю, что это должно учитываться как два неявных преобразования и, следовательно, не допускается, но выходные данные компилятора не предполагают этого вообще.
1 ответ
Во-первых, неправильно звонить test2::test2(string)
"явный конструктор преобразования". Он будет использоваться в неявных преобразованиях (отметьте его explicit
если ты этого не хочешь).
Во всяком случае, сообщение об ошибке Clang на месте, и он почти идеально объясняет, что происходит.
Это:
test2 t2(t1);
называется прямой инициализацией. Все конструкторы для test2
являются кандидатами и, кроме того, компилятор может запустить последовательность неявных преобразований в соответствии с аргументами. Находит test1::operator string
а также test2::test(string)
и все хорошо.
Это:
test2 t2 = t1;
называется инициализация копирования. Выражение справа от =
должен быть преобразован в test2
и тогда для конструирования объекта будет вызван либо конструктор копирования, либо перемещение (по крайней мере, теоретически он может быть позднее исключен как оптимизация, но тем не менее он должен быть доступен).