прямая инициализация против инициализации прямого списка (C++)

DIRECT- VS COPY-INITIALIZATION
Через этот вопрос (Это прямая инициализация или инициализация копированием?) Я узнал разницу между прямой инициализацией и инициализацией копированием:

direct-initialization                   copy-initialization
-----------------------                 ---------------------

obj s("value");                         obj s = obj("value");
                                        obj s = "value";

obj s{"value"};                         obj s = {"value"};
                                        obj s = obj{"value"};

Я упоминаю об этом здесь для полноты картины. Мои фактические вопросы для этой страницы перечислены в следующем абзаце >>


ПРЯМАЯ ИНИЦИАЛИЗАЦИЯ И ПРЯМАЯ ИНИЦИАЛИЗАЦИЯ СПИСКА
Ответы показали, что в категории прямой инициализации можно провести различие между прямой инициализацией и прямая-инициализация-списка.:

obj s("value");   // direct-initialization

obj s{"value"};   // direct-list-initialization

Я знаю, что инициализация списка не допускает сужения, так что инициализация типа int x{3.5}; не будет компилироваться. Но кроме этого, у меня есть пара вопросов:


(1) Есть ли разница в выводе компилятора между
obj s("value"); и obj s{"value"};?
Давайте рассмотрим компилятор без каких-либо оптимизаций. Я хотел бы знать любые возможные технические различия :-)


(2) Возможно, мне следует задать тот же самый вопрос для инициализации с несколькими переменными, например:
obj s("val1", "val2"); и obj s{"val1", "val2"};


(3) Я заметил, что при инициализации списка иногда может вызываться другой конструктор, например:

vector<int> a{10,20};   //Curly braces -> fills the vector with the arguments
vector<int> b(10,20);   //Parentesis -> uses arguments to parameterize some functionality

Как это возможно?

РАССМОТРИЛИ ЗДЕСЬ ВСЕ ВОЗМОЖНЫЕ ИНИЦИАЛИЗАЦИИ?
Исходя из моих ограниченных знаний о C++, я полагаю, что все возможные инициализации объектов (как с собственным типом, так и с типом, определяемым пользователем) были рассмотрены в примеры выше. Это правильно? Я что-то проглядел?


PS: я изучаю C++ (я знаю C, но еще не C++), поэтому, пожалуйста, не ругайте меня ;-)


person K.Mulier    schedule 18.11.2017    source источник
comment
Я знаю, что инициализация списка не допускает сужения, так что инициализация типа int x{3.5}; не компилируется. Это не совсем так. Стандарт предписывает только диагностику, но компиляторам полностью разрешено продолжать и компилировать код. Например, g++ предупредит, но продолжит. В любом случае, ИМО, этот вопрос слишком широк, и на многие его отдельные элементы уже есть ответы в другом месте. И если вы хотите знать, будет ли компилятор создавать другой код... просто попробуйте?   -  person underscore_d    schedule 21.11.2017
comment
Привет @underscore_d, я согласен с тобой, что этот вопрос довольно широк. Но я думаю, что в этом виноват C++. Язык предоставляет оооочень много способов инициализировать вещи - иногда различия незначительны, но имеют некоторое влияние, иногда различия только синтаксические. Все это довольно сложно для кого-то вроде меня (новичок в C++). Ответы на многие отдельные элементы действительно есть где-то еще, и именно в этом проблема. Информация разбросана по округе. Хотелось бы все в одном месте... :-)   -  person K.Mulier    schedule 22.11.2017


Ответы (2)



(1) Есть ли разница в выводе компилятора между
obj s("value"); и obj s{"value"};?
Давайте рассмотрим компилятор без каких-либо оптимизаций. Я хотел бы знать любые возможные технические различия :-)


(2) Возможно, мне следует задать тот же самый вопрос для инициализации с несколькими переменными, например:
obj s("val1", "val2"); и obj s{"val1", "val2"};


(3) Я заметил, что при инициализации списка иногда может вызываться другой конструктор, например:

vector<int> a{10,20};   //Curly braces -> fills the vector with the arguments
vector<int> b(10,20);   //Parentesis -> uses arguments to parameterize some functionality

Как это возможно?

  • Если есть конструктор списка инициализаторов для типа класса obj он будет всегда предпочтительнее других конструкторов для brace-init-initializers (инициализация списка) в вашем дело obj s{"value"};;

    Это означает, что если у вас есть конструктор, который принимает std::initializer_list<T> в качестве первого параметра, а остальные параметры установлены по умолчанию, то он предпочтительнее. Пример

    struct A{
        A(std::initializer_list<std::string>);    //Always be preferred for A a{"value"}
        A(std::string);
    };
    

    std::vector<T> и другие контейнеры STL имеют такие конструкторы списка инициализаторов.

  • В противном случае срабатывает разрешение перегрузки, и он возвращается к любому доступному конструктору, выбранному процесс разрешения перегрузки;

  • В противном случае, если класс не имеет определяемых пользователем конструкторов и является агрегатным типом, он инициализирует члены класса напрямую.


РАССМОТРИЛИ ЗДЕСЬ ВСЕ ВОЗМОЖНЫЕ ИНИЦИАЛИЗАЦИИ?
Исходя из моих ограниченных знаний о C++, я полагаю, что все возможные инициализации объектов (как с собственным типом, так и с типом, определяемым пользователем) были рассмотрены в примеры выше. Это правильно? Я что-то проглядел?

Неа. ты этого не сделал. За исключением ссылочной инициализации, существует пять способов инициализации объектов в C++.

  • Прямая инициализация
  • Инициализация списка
  • Копировать инициализацию
  • Инициализация значения
  • Агрегатная инициализация (только для агрегатных типов)

Дополнительную информацию можно найти здесь

person WhiZTiM    schedule 18.11.2017
comment
Я хотел бы добавить, что инициализация списка гарантирует порядок оценки аргументов слева направо, поэтому, если вам нужно извлечь аргументы из потокового ресурса, вы должны сделать это вне скобок конструктора или использовать инициализацию списка. - person Semyon Burov; 18.11.2017
comment
Привет, @SemyonBurov, очень интересное замечание, которое ты здесь делаешь. Я этого не знал. Не могли бы вы объяснить больше, возможно, привести какой-то пример? Было бы здорово опубликовать это в ответе и получить заслуженные баллы ;-) - person K.Mulier; 18.11.2017
comment
Привет @WhiZTiM, большое спасибо за интересный ответ. Итак, правильно ли я говорю, что list-initialization — это механизм, позволяющий перегружать конструктор? Механизм, позволяющий отличить vector<int> a{10,20}; от vector<int> b(10,20);. Или такой взгляд на инициализацию списка слишком упрощен? - person K.Mulier; 18.11.2017
comment
@K.Mulier, тогда я добавлю это как ответ - person Semyon Burov; 18.11.2017
comment
@K.Mulier, концепция list-initialization заключается в том, чтобы включить заполнение контейнеров или объектов элементами во время инициализации; Если вы ознакомитесь с правилами, регулирующими его поведение, вы выяснить, что он может вернуться (хотя технически это и недопустимо) к другим методам инициализации. Таким образом, если бы в стандартной библиотеке std::vector не было конструктора списка инициализаторов, то vector<int> a{10,20}; и vector<int> b(10,20); фактически были бы одинаковыми. Так что да, предоставление таких конструкторов списка инициализаторов имеет значение. - person WhiZTiM; 19.11.2017

Инициализация списка гарантирует порядок оценки аргументов слева направо. В этом примере мы создадим std::tuple из istream данных, а затем выведем кортеж пример можно найти здесь:

#include <iostream>
#include <sstream>
#include <tuple>

template<typename T, typename CharT>
T extract(std::basic_istream<CharT>& is) {
    T val;
    is >> val;
    return val;
}

void print(const std::tuple<int, long, double>& t) {
    using std::cout;
    cout << std::get<0>(t) << " " << std::get<1>(t) << " " << std::get<2>(t) << std::endl;
}

int main()
{
    std::stringstream ss1;
    std::stringstream ss2;
    ss1 << 1 << " " << 2 << " " << 3;
    ss2 << 1 << " " << 2 << " " << 3;
    auto compilerOrder    = std::tuple<int, long, double>( extract<int>(ss1), extract<long>(ss1), extract<double>(ss1) );
    auto leftToRightOrder = std::tuple<int, long, double>{ extract<int>(ss2), extract<long>(ss2), extract<double>(ss2) };
    print(compilerOrder);
    print(leftToRightOrder);
}

Выход:

3 2 1
1 2 3

Как видите, разница будет заметна, если мы несколько раз будем использовать один и тот же потокоподобный ресурс внутри функциональных скобок.

Также мой вопрос по этому поводу

person Semyon Burov    schedule 18.11.2017
comment
Очень интересно! - person K.Mulier; 18.11.2017