C++: выбор `const char *` vs `std::string` при использовании делегирующего конструктора

Учтите следующее:

class Foo {
public:
    Foo (const char *in) {
        printf ("C string constructor called\n");
    }
    Foo (std::string const &in) : Foo(in.c_str()) {
        printf ("C++ string constructor called\n");
    }
};
Foo bar ("I never asked for this");
//C string constructor called

Итак, постоянная string рассматривается как const char * один.

Но что изменится, если мы сделаем std::string конструктор "первичный"?

Можем ли мы ожидать, что std::string объект будет создан и передан соответствующему конструктору без вызова связанной с C-строкой?

class Foo {
public:
    Foo (std::string const &in) {
        printf ("C++ string constructor called\n");
    }
    Foo (const char *in) : Foo(std::string (in)) {
        printf ("C string constructor called\n");
    }
};
Foo bar ("I never asked for this");
//C++ string constructor called
//C string constructor called

Опять же, конструктор C-строки был вызван первым.

Это поведение описано в стандарте C++, или это связано с компилятором?

Будет ли это работать так же, например, для шаблонов или перегруженных функций?

Я скомпилировал с GCC 7.3.0 (MSYS2 x64).

3 ответа

Решение

"I never asked for this" является строковым литералом, который состоит из const char элементы:

Foo bar ("I never asked for this"); // calls Foo (const char *in)

Таким образом, Foo (const char *in) всегда будет выбираться по разрешению перегрузки независимо от "порядка", в котором вы объявляете свои конструкторы.

Как видно из вашего второго примера,

Foo (const char *in) : Foo(std::string (in))

Делегирующий конструктор выбран и будет вызывать целевой конструктор, выбранный единственным членом списка инициализации.

В C++ нет такого понятия, как первичный конструктор.

Вы наблюдаете, что делегированное телу конструктора (целевого конструктора) выполняется первым. Затем тело конструктора, который делегирует (делегирующий конструктор).

Делегирующий конструктор:

В этом случае целевой конструктор выбирается с помощью разрешения перегрузки и выполняется первым, затем элемент управления возвращается к делегирующему конструктору, и его тело выполняется.

Разрешение перегрузки и делегирование конструктора - это две совершенно разные вещи, которые вообще не влияют друг на друга.

Разрешение перегрузки позволяет избежать неявных преобразований, когда это возможно.

Строковый литерал как "I never asked for this" это const char[]который разлагается на const char *, Это точное соответствие для вашего const char * конструктор, так что это тот, который вызывается. Зовет ваш std::string конструктор со строковым литералом в качестве входных данных потребует неявного преобразования, поскольку компилятор должен будет создать временный std::string объект связать с std::string const & ссылка.

Если бы вы написали этот код вместо этого:

Foo bar (std::string("I never asked for this"));

Или это:

std::string str = "I never asked for this";
Foo bar (str);

Тогда std::string const & конструктор будет вызываться вместо const char * конструктор, так как нет неявного преобразования из std::string в const char *,

То, как конструкторы делегируют друг другу, является деталью реализации ПОСЛЕ того, как компилятор решает, какой конструктор вызвать.

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