Инициализация вложенного списка не соответствует конструктору, принимающему initializer_list в качестве второго аргумента

Я разрабатываю структуру данных, способную описать семантику некоторых файлов XML в C ++. Идея заключается в проверке наличия и / или правильной последовательности различных элементов при сохранении текста, который они содержат, в объект QHash с идентификаторами QString (на основе имен элементов, но настраиваемых) в качестве ключей.

Поскольку XML поддерживает вложение, я бы хотел иметь возможность отражать это вложение. Таким образом, каждый элемент XML описывается либо «name» и (необязательно) «id», что означает, что это последний лист и его текст будет проанализирован, либо «name» и списком других дескрипторов элемента, что означает, что должны быть эти вложенные элементы внутри текущего.

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

Моя идея состояла в том, чтобы иметь класс, который описывает один элемент и может быть создан с помощью литералов c ++ std :: initializer_list, что я надеялся неявно поддерживать вложения. Различные перегруженные конструкторы, которые могут позже установить различные конкретные детали.

Я добрался почти до цели, но теперь застрял. Хотя конструктор с подписью constructor(std::initializer_list<ProtocolDescriptorTestNode >);
обычно вызывается для всех вложенных фигурных скобок, конструктор сигиллярной подписи constructor(QString, std::initializer_list<ProtocolDescriptorTestNode >);
никогда не вызывается, даже если я размещаю конструкции как
{ "xyz", { {"abc", "123"}, {"def","456"} } }

в качестве инициала литерала.

Пожалуйста, посмотрите на следующие фрагменты кода, отдельно от кода тестирования, и помогите мне понять, если:
1. Это нормальное поведение c ++ 11, и std :: initializer_list не поддерживает такое вложение в сочетании с другими параметрами типа данных.
2. Это вопрос реализации (bug;)) в gcc (я использую версию 4.9.1 (Debian 4.9.1-1))
3. Я пропускаю некоторые действительно глупые детали в синтаксисе / семантике кода.

Объявление класса (выдержка из соответствующих конструкторов):

class ProtocolDescriptorTestNode {

public:
ProtocolDescriptorTestNode(const ProtocolDescriptorTestNode&);

ProtocolDescriptorTestNode(std::initializer_list<ProtocolDescriptorTestNode > init); // 1
ProtocolDescriptorTestNode(QString name, std::initializer_list<ProtocolDescriptorTestNode > init); // 2
ProtocolDescriptorTestNode(QString name, QString id, enum eElementMode = modeDefault); // 4
ProtocolDescriptorTestNode(QString name, enum eElementMode = modeDefault); //5

~ProtocolDescriptorTestNode() {}

QString name, id;
tProtocolDescriptorTestList list;
};

Определения соответствующих конструкторов:

ProtocolDescriptorTestNode::ProtocolDescriptorTestNode(std::initializer_list<ProtocolDescriptorTestNode> init)
{
qDebug() << "*** CONSTRUCTOR CALLED - 1 *** ";
qDebug() << init.size();
for(ProtocolDescriptorTestNode x : init) {
qDebug() << x.name << x.id;
}
}

ProtocolDescriptorTestNode::ProtocolDescriptorTestNode(QString name, std::initializer_list<ProtocolDescriptorTestNode> init) {
qDebug() << "*** CONSTRUCTOR CALLED - 2 *** ";
qDebug() << init.size();
for(ProtocolDescriptorTestNode x : init) {
qDebug() << x.name << x.id;
}
}

ProtocolDescriptorTestNode::ProtocolDescriptorTestNode(QString name, QString id, enum eElementMode) :
name(name),
id(id)
{
qDebug() << "*** CONSTRUCTOR CALLED - 4 *** ";
qDebug() << name << id;
}

ProtocolDescriptorTestNode::ProtocolDescriptorTestNode(QString name, enum eElementMode)  :
name(name),
id("***")
{
qDebug() << "*** CONSTRUCTOR CALLED - 5 *** ";
qDebug() << name << id;
}

Экземпляр объекта тестирования:
(обратите внимание: неявное / явное преобразование типов данных char * / QString не имеет значения)

ProtocolDescriptorTestNode groupOther
({
{QString("name1"),"groupOther1"},
{"name2","groupOther2"},

{ QString("B"), {
{"name3","groupOther3"},
{
{"intra1","a"},
{QString("intra2")}
},
{"name4","groupOther4"}
} }

});

И соответствующая часть отладочного вывода, показывающая, что часть инициализации рядом с "B" литерал трактуется как node(QString("B") а также node(std::initializer_list) сцепленный, а не node(QString("B"), std::initializer_list) как было мое намерение:

*** CONSTRUCTOR CALLED - 4 ***
"name1" "groupOther1"*** CONSTRUCTOR CALLED - 4 ***
"name2" "groupOther2"*** CONSTRUCTOR CALLED - 5 ***
"B" "***"*** CONSTRUCTOR CALLED - 4 ***
"name3" "groupOther3"*** CONSTRUCTOR CALLED - 4 ***
"intra1" "a"*** CONSTRUCTOR CALLED - 5 ***
"intra2" "***"*** CONSTRUCTOR CALLED - 1 ***
1
"intra2" "***"*** CONSTRUCTOR CALLED - 1 ***
2
"intra1" "a""" ""*** CONSTRUCTOR CALLED - 4 ***
"name4" "groupOther4"*** CONSTRUCTOR CALLED - 1 ***
3
"name3" "groupOther3""" """name4" "groupOther4"*** CONSTRUCTOR CALLED - 1 ***
2
"B" "***""" ""*** CONSTRUCTOR CALLED - 1 ***
3
"name1" "groupOther1""name2" "groupOther2""" ""

2

Решение

Проблема, с которой вы сталкиваетесь, заключается в том, что если у типа есть конструктор с одним параметром типа initializer_list (или имеет дополнительные параметры, но остальные имеют аргументы по умолчанию), При инициализации списка всегда предпочтет этот конструктор над другими конструкторами (§13.3.1.7 / 1 [over.match.list]). Имея это в виду, давайте проработаем вашу инициализацию.

На внешнем уровне у вас есть Braced-init-list, который содержит 3 элемента:

{
{QString("name1"),"groupOther1"}  // element 1
{"name2","groupOther2"}           // element 2
{ QString("B"), ... }             // element 3
}

ProtocolDescriptorTestNode имеет конструктор, который принимает один аргумент типа initializer_list<ProtocolDescriptorTestNode>, Из-за правила, которое я упоминал ранее, компилятор попытается преобразовать каждый из этих трех элементов в ProtocolDescriptorTestNode,

Каждый из этих элементов сам по себе является скобками-init-списками, поэтому будет сделана попытка сопоставить initializer_list конструктор первый.


Рассмотрим первый элемент:

{QString("name1"),"groupOther1"}

Чтобы преобразовать это в initializer_list<ProtocolDescriptorTestNode>, второй аргумент потребует 2 пользовательских преобразования, сначала QString а затем ProtocolDescriptorTestNode, что не допускается. Так что другие конструкторы ProtocolDescriptorTestNode считаются, и это соответствует конструктор 4.

Обратите внимание, что поведение было бы другим, если бы первый элемент был

 {QString("name1"),QString("groupOther1")}

В этом случае каждый элемент списка braced-init-list будет соответствовать конструктор 5 создать ProtocolDescriptorTestNode экземпляр, и тогда они будут образовывать initializer_list<ProtocolDescriptorTestNode> и соответствовать конструктор 1.


Следующий элемент

{"name2","groupOther2"}

который снова совпадает конструктор 4 по той же причине, что и в последнем случае.


Третий элемент

 { QString("B"),
{
{"name3","groupOther3"},  // constructor 4
{
{"intra1","a"},        // constructor 4
{QString("intra2")}    // constructor 1
},                        // constructor 1
{"name4","groupOther4"}   // constructor 4
}                            // constructor 1
}                               // constructor 1

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

таким образом конструктор 2 никогда не совпадает.


После этого многословного объяснения решение вашей проблемы на удивление просто и намекается на объяснение первого элемента. Причина элемент 3 соответствует initializer_list Конструктор потому, что оба его подэлемента неявно преобразуются в ProtocolDescriptorTestNode, Итак, замени QString("B") с "B", Теперь его преобразование в ProtocolDescriptorTestNode требует двух пользовательских преобразований, и initializer_list Конструктор больше не жизнеспособен. Другие конструкторы будут рассмотрены, и конструктор 2 будет соответствовать.

Живая демо

0

Другие решения


По вопросам рекламы ammmcru@yandex.ru
Adblock
detector