Подтвердить что ты не робот

Что такое конструктор преобразования в С++? Для чего это?

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

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

 int main()
{
    MyClass M = 1 ;
}
4b9b3361

Ответ 1

Определение для конструктора преобразования отличается от С++ 03 и С++ 11. В обоих случаях он должен быть конструктором не explicit (иначе он не будет задействован в неявных преобразованиях), но для С++ 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 - оба преобразования конструкторов в С++ 03 и С++ 11. Конструктор 3, который должен принимать два аргумента, является только конструктором преобразования в С++ 11. Последний, конструктор 4, не является конструктором преобразования, потому что он explicit.

  • С++ 03: §12.3.1

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

  • С++ 11: §12.3.1

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

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

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

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

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

  • С++ 11: §6.6.3:

    Оператор

    A return с бин-init-list инициализирует объект или ссылку, возвращаемую из функции путем инициализации-списка-инициализации (8.5.4) из указанного списка инициализаторов.

    §8.5:

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

    §12.3.1:

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

Ответ 2

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

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

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 ключевое слово всегда используется для предотвращения неявного преобразования для конструктора и применяется к конструктору в объявлении класса.

Ответ 3

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