Почему оператор преобразования не вызывается при использовании синтаксиса инициализации и почему сообщение об ошибке clang кажется неверным?
-
20-12-2019 - |
Вопрос
У меня есть следующий код, который создает один объект t2 с использованием конструктора явного преобразования, который выполняет неявное преобразование t1.Это ожидаемо и описано в разделе 11.4.1 третьего издания «Язык программирования C++».
#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() {
Что дает?А что говорит стандарт C++ о неявных преобразованиях в конструкторах инициализации?я предполагать что это следует считать двумя неявными преобразованиями и, следовательно, не разрешать, но вывод компилятора вообще не предполагает этого.
Решение
Во-первых, неправильно звонить test2::test2(string)
а «конструктор явного преобразования».Он будет использоваться в неявных преобразованиях (отметьте его explicit
если ты этого не хочешь).
В любом случае, сообщение об ошибке clang точное и почти идеально объясняет, что происходит.
Этот:
test2 t2(t1);
называется прямая инициализация.Все конструкторы для test2
являются кандидатами, и, кроме того, компилятор может выполнить последовательность неявных преобразований для соответствия аргументам.Он находит test1::operator string
и test2::test(string)
и все хорошо.
Этот:
test2 t2 = t1;
называется копирование инициализации.Выражение справа от =
необходимо преобразовать в test2
а затем для создания объекта будет вызван либо конструктор копирования, либо конструктор перемещения (по крайней мере, теоретически его позже можно исключить из оптимизации, но, тем не менее, он должен быть доступен).