Почему оператор преобразования не вызывается при использовании синтаксиса инициализации, и почему сообщение об ошибке 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 и тогда для конструирования объекта будет вызван либо конструктор копирования, либо перемещение (по крайней мере, теоретически он может быть позднее исключен как оптимизация, но тем не менее он должен быть доступен).

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