В приведенном ниже примере я анализирую объект json, используя boost::json
.
Когда я печатаю тип возвращаемого boost::json::value
, он имеет тип object
, как и ожидалось.
Затем у меня есть 2 класса, которые идентичны во всех отношениях, за исключением того, что в BraceInit
я инициализирую свой член boost::json::value
с помощью инициализации фигурных скобок, а в ParenInit
я инициализирую свой член boost::json::value
с помощью скобок.
Использование инициализации скобок приводит к тому, что мой object
преобразуется в array
размера 1, содержащий мой исходный object
.
#include <iostream>
#include <boost/json.hpp>
namespace json = boost::json;
void print_type(const json::value& jv)
{
switch (jv.kind())
{
case json::kind::object: std::cout << "object\n"; break;
case json::kind::array: std::cout << "array\n"; break;
default: std::cout << "other\n"; break;
}
};
struct BraceInit
{
BraceInit(json::value jv)
: _jv{jv}
{
print_type(_jv);
}
json::value _jv;
};
struct ParenInit
{
ParenInit(json::value jv)
: _jv(jv)
{
print_type(_jv);
}
json::value _jv;
};
int main()
{
const std::string str = R"({ "foo": "bar" })";
const json::value jv = json::parse(str);
print_type(jv);
BraceInit b(jv);
ParenInit p(jv);
return 0;
}
Выход:
object array object
Что здесь происходит?
Моя «инициализация скобок» на самом деле не выполняет инициализацию скобок, как я ожидаю, а создает std::initializer_list
размера 1?
От [class.base.init]/7
The expression-list or braced-init-list in a mem-initializer is used to initialize the designated subobject (or, in the case of a delegating constructor, the complete class object) according to the initialization rules of 11.6 for direct-initialization.
11.6 здесь относится к [dcl.init]
, который в пункте 17 гласит:
The semantics of initializers are as follows. The destination type is the type of the object or reference being initialized and the source type is the type of the initializer expression. If the initializer is not a single (possibly parenthesized) expression, the source type is not defined.
(17.1) — If the initializer is a (non-parenthesized) braced-init-list or is = braced-init-list, the object or reference is list-initialized (11.6.4).
11.6.4 просто относится к [dcl.init.list]
.
Итак, для меня это звучит так, как будто _jv{jv}
вызывает версию конструктора списка инициализации (если она существует), которая, согласно повысить документацию, создает массив.
Примечание: все элементы из черновика C++17 N4659.
Вы можете увидеть это в упрощенной версии здесь: https://godbolt.org/z/9qrf3EooY
Обновлено: cppreference упрощает это до:
A std::initializer_list object is automatically constructed when:
a braced-init-list is used to list-initialize an object, where the corresponding constructor accepts an std::initializer_list parameter
Хорошее объяснение. Я до сих пор не могу понять, почему инициализация в C++ должна быть такой кашей. Аналогичная ловушка — std::vector<int> v{2}
против std::vector<int> v(2)
.
Спасибо, редактирование cppreference действительно проясняет это.