C ++ - есть ли обходной путь для делегирования конструкторов, которые не являются единственными членами в списке инициализации - PullRequest
0 голосов
/ 10 марта 2020
class Foo
{
public:
  Foo() {}
  Foo(int i) 
   : Foo(), m_bar(0) {}

private:
  int m_bar;
};

Есть ли обходной путь, чтобы сделать такой код действительным?

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

Ответы [ 2 ]

2 голосов
/ 10 марта 2020

Правило, на которое вы ссылаетесь, определено в [class.base.init]/6:

Список mem-initializer-list может делегировать другому конструктору класса конструктора, используя любой класс-или-decltype, который обозначает сам класс конструктора. Если mem-initializer-id обозначает класс конструктора, он должен быть единственным mem-initializer; конструктор является делегирующим конструктором, а конструктор, выбранный mem-initializer, является целевым конструктором. Целевой конструктор выбирается по разрешению перегрузки. Как только целевой конструктор возвращается, тело делегирующего конструктора выполняется. Если конструктор делегирует себя прямо или косвенно, программа некорректна, диагностика не требуется c.

Предположим, что конструктор делегата можно использовать вместе с другими инициализаторами mem. :

  • Вы можете вызвать двух разных делегатов. Но может быть только одна конструкция. Таким образом, у вас будет конфликт, какой из них выбрать.
  • с mem-инициализаторами после делегирования, когда делегат завершит sh свою работу, выполнение делегирующего конструктора будет возобновлено не с телом, а с остальными mem-инициализаторами.
  • Но инициализаторы mem не похожи на назначения, в которых одно назначение может перезаписать другое. Mem-инициализаторы вызывают конструкторы членов. И переменные-члены могут быть построены только один раз. Тогда возникнет конфликт между тем, как инициализировать элементы в списке, и какой конструктор следует отбросить.
  • Это еще хуже, поскольку как делегирующие, так и делегированные конструкторы должны полностью создавать класс boject, то есть всех его членов.

Вот пример для выяснения проблемы с элементом, который может быть инициализирован только один раз, и решения:

class Foo
{
  const int m_bar;              // can be initalized only once 
  int m_zoo;                    // must be constructed (default possible) but can be overwritten
public:
  Foo(): m_bar(1), m_zoo(2) {}  // the constant can never be changed
  Foo(int i) 
   : Foo() {m_zoo=i;}           // you can still change in the body already constructed items
  Foo(int i, int j)            // comprehensive init 
    : m_bar(i), m_zoo(j) {}    // (has all that delegating may want(
  Foo(char a) 
    : Foo(a,2) {};             // delegate to the comprehensive ctor
};

Демонстрационная версия

Правило делегирования направлено на то, чтобы все было просто. Это просто означает, что

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

Так способ сделать ваш проект корректным - переместить весь mem-initilizer в целевой конструктор и при необходимости использовать параметры этого целевого конструктора, чтобы указать значения для его mem-инициализаторов. Или переписать в теле то, что уже было инициализировано; но это не всегда возможно.

1 голос
/ 10 марта 2020

Проблема в том, что список инициализатора в конструкторе должен инициализировать каждый базовый элемент и элемент (в порядке объявления), который имеет свой собственный конструктор (конструктор по умолчанию, если не указано иное), что : Foo() уже должен был это сделать, и вызов некоторые члены конструктора дважды были бы плохими.

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

...