Что такое конвертирующий конструктор в C++? Для чего это?

Я слышал, что в C++ есть нечто, называемое "конструкторами преобразования" или "конструкторами преобразования". Что это и для чего они? Я видел упомянутое в отношении этого кода:

class MyClass
{
  public:
     int a, b;
     MyClass( int i ) {}
}

 int main()
{
    MyClass M = 1 ;
}

2 ответа

Решение

Определение конвертирующего конструктора отличается в C++03 и C++11. В обоих случаях это должно бытьexplicit конструктор (иначе он не будет вовлечен в неявные преобразования), но для C++03 он также должен вызываться с одним аргументом. То есть:

struct foo
{
  foo(int x);              // 1
  foo(char* s, int x = 0); // 2
  foo(float f, int x);     // 3
  explicit foo(char x);    // 4
};

Конструкторы 1 и 2 являются конвертирующими конструкторами в C++03 и C++11. Конструктор 3, который должен принимать два аргумента, является только конвертирующим конструктором в C++11. Последний, конструктор 4, не является конвертирующим конструктором, потому что он explicit,

  • C++03: §12.3.1

    Конструктор, объявленный без спецификатора функции explicit это может быть вызвано с единственным параметром, определяющим преобразование от типа его первого параметра к типу его класса. Такой конструктор называется конвертирующим конструктором.

  • C++ 11: §12.3.1

    Конструктор, объявленный без спецификатора функции explicit определяет преобразование из типов его параметров в тип своего класса. Такой конструктор называется конвертирующим конструктором.

Почему конструкторы с более чем одним параметром считаются конвертирующими конструкторами в C++11? Это связано с тем, что новый стандарт предоставляет нам удобный синтаксис для передачи аргументов и возврата значений с использованием braced-init-lists. Рассмотрим следующий пример:

foo bar(foo f)
{
  return {1.0f, 5};
}

Возможность указать возвращаемое значение в виде фигурного списка инициализации считается преобразованием. Это использует конструктор преобразования для foo это занимает float и int, Кроме того, мы можем вызвать эту функцию, выполнив bar({2.5f, 10}), Это тоже конверсия. Поскольку они являются преобразованиями, имеет смысл использовать конструкторы, которые они используют, для преобразования конструкторов.

Поэтому важно отметить, что создание конструктора foo который занимает float и int иметь explicit спецификатор функции остановит приведенный выше код от компиляции. Приведенный выше новый синтаксис можно использовать только в том случае, если для выполнения работы доступен конструктор преобразования.

  • C++ 11: §6.6.3:

    return Оператор с фигурным списком инициализации инициализирует объект или ссылку, которые должны быть возвращены из функции путем копирования-инициализации списка (8.5.4) из указанного списка инициализатора.

    §8.5:

    Инициализация, которая происходит [...] при передаче аргумента [...], называется инициализацией копирования.

    §12.3.1:

    Явный конструктор создает объекты точно так же, как неявные конструкторы, но делает это только там, где явно используется синтаксис прямой инициализации (8.5) или где приведено приведение (5.2.9, 5.4).

Неявное преобразование с помощью конструктора преобразования

Давайте сделаем пример в вопросе более сложным

class MyClass
{
  public:
     int a, b;
     MyClass( int i ) {}
     MyClass( const char* n, int k = 0 ) {}
     MyClass( MyClass& obj ) {}
}

Первые два конструктора являются конвертирующими конструкторами. Третий - это конструктор копирования, и, как таковой, это еще один конструктор преобразования.

Конвертирующий конструктор обеспечивает неявное преобразование из типа аргумента в тип конструктора. Здесь первый конструктор обеспечивает преобразование из int к объекту класса MyClass, Второй конструктор позволяет преобразовать строку в объект класса MyClass, И третье... от объекта класса MyClass к объекту класса MyClass!

Чтобы быть конвертирующим конструктором, конструктор должен иметь один аргумент (во втором случае второй аргумент имеет одно значение по умолчанию) и должен быть объявлен без ключевого слова explicit,

Тогда инициализация в main может выглядеть так:

int main()
{
    MyClass M = 1 ;
    // which is an alternative to
    MyClass M = MyClass(1) ;

    MyClass M = "super" ;
    // which is an alternative to
    MyClass M = MyClass("super", 0) ;
    // or
    MyClass M = MyClass("super") ;
}

Явное ключевое слово и конструкторы

Теперь, что если бы мы использовали explicit ключевое слово?

class MyClass
{
  public:
     int a, b;
     explicit MyClass( int i ) {}
}

Тогда компилятор не примет

   int main()
    {
        MyClass M = 1 ;
    }

так как это неявное преобразование. Вместо этого нужно написать

   int main()
    {
        MyClass M(1) ;
        MyClass M = MyClass(1) ;
        MyClass* M = new MyClass(1) ;
        MyClass M = (MyClass)1;
        MyClass M = static_cast<MyClass>(1);
    }

explicit Ключевое слово всегда должно использоваться для предотвращения неявного преобразования для конструктора, и оно применяется к конструктору в объявлении класса.

Конструктор преобразования - это конструктор с одним параметром, который объявлен без явного спецификатора функции. Компилятор использует конструкторы преобразования для преобразования объектов из типа первого параметра в тип класса конструктора преобразования.

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