Вектор С++ инициализируется значением

Как новичок в C++, я знаю, что мы можем инициализировать вектор векторов, используя:

vector<vector<int>> vec(10, vector<int>(15, 0));

что достаточно эффективно, но когда я хочу сделать то же самое с написанной мной структурой, код не скомпилируется:

vector<TreeNode> vec(10, TreeNode("abc"));

структура выглядит так:

struct TreeNode {
    std::string val;
    TreeNode* left;
    TreeNode* right;
    explicit TreeNode(std::string& val) {
        this->val = val;
        left = nullptr, right = nullptr;
    }

};

мне интересно в чем тут проблема

Я использовал Clion в качестве своей IDE, IDE сообщала об этой ошибке: No matching conversion for functional-style cast from 'const char [4]' to 'TreeNode'

И компилятор сообщал об этой ошибке:

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

ChrisMM 13.12.2020 00:36

Просто предположение, используйте const std::string &val в качестве параметра конструктора.

ChrisMM 13.12.2020 00:37

Это не имеет ничего общего с vector. Просто TreeNode("abc"); уже не компилируется.

Igor Tandetnik 13.12.2020 00:40

Похоже, в первую очередь вам не хватает const в параметре конструктора val: coliru.stacked-crooked.com/a/3e838a31605cdb1f

πάντα ῥεῖ 13.12.2020 00:43

что весьма эффективно" Нужна цитата! Я всегда рекомендую против таких вложенных векторов. Это совершенно не нужно с кучей косвенных и накладных расходов.

Asteroids With Wings 13.12.2020 00:47

@ChrisMM Спасибо за ответ, я думаю, что это решает вопрос. в дополнение к этому вопросу, знаете ли вы, что такое vector<int>(2, 0) или TreeNode("str") в выражении? Это временный объект или какой-то конструктор?

user542598 13.12.2020 00:48

@ πάνταῥεῖ Я просто предполагаю, что он не присваивает значения и не вставляет векторы вручную в вектор, например vec.push(vector<int>(15, 0). Думал, эффективно. Мне интересно, на что похожи косвенность и накладные расходы?

user542598 13.12.2020 00:53

@user542598 user542598 Оба они временные. Да, создание временного объекта требует вызова конструктора. Будучи педантичным, хотя многие люди скажут, что код сам по себе является «вызовом конструктора», это не так; это просто похоже на один.

Asteroids With Wings 13.12.2020 00:58
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
8
473
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Проблема не в инициализации std::vector (что правильно), а в инициализации структуры TreeNode.

Этот измененный код работает:

std::string initVal = "abc";
std::vector<TreeNode> vec(10, TreeNode(initVal));

Итак, источник проблемы?

πάντα ῥεῖ 13.12.2020 00:45

Это потому, что VS позволяет привязывать временные объекты к lvalue refs-to-non-const. Это не стандартный С++.

Asteroids With Wings 13.12.2020 00:47

@AsteroidsWithWings Не похоже, что это связано с расширением, поскольку существует реальная переменная правильного типа для привязки к ссылке, а не к исходной временной.

Retired Ninja 13.12.2020 00:52

@AsteroidsWithWings: работает и в других компиляторах.

friol 13.12.2020 00:55

@RetiredNinja Ах, я не заметил, что Габриэле предоставляет свежий код. Я прочитал ответ о том, что исходный код работает в VS. Виноват.

Asteroids With Wings 13.12.2020 00:56

Тем не менее, это не лучший ответ, так как он даже не пытается объяснить, почему проблема существует и почему этот измененный код исправляет ее.

Asteroids With Wings 13.12.2020 00:56
Ответ принят как подходящий

Строковый литерал вроде "abc" не является std::string. Но это нормально! std::string, например тот, который является параметром в вашем конструкторе TreeNode, может быть создан из него автоматически.

Однако эта строка будет временной, а временные строки не привязываются к ссылкам, отличным от const (за исключением расширения Visual Studio).

Это тоже нормально: они вам не нужны; вместо этого ваш конструктор может взять const std::string& и все будет хорошо.

Другие решения включают в себя получение std::string по значению, что на самом деле является хорошей идеей, поскольку тогда вы можете std::move из него внутри конструктора, и теперь ваш конструктор эффективен независимо от того, передаете ли вы временную или перемещаете какую-либо другую именованную строку. Но это история для другого дня.

Кроме того, если этот проект предназначен для производства и у вас, вероятно, будет намного больше векторов, чем показано в вашем примере, я настоятельно рекомендую вам не вкладывать их таким образом. Каждый элемент имеет собственное управление памятью и косвенное обращение, что очень расточительно. Ваши данные имеют квадратную форму (внутреннее измерение всегда имеет одинаковый размер), поэтому все, что вам действительно нужно, — это один вектор размером 10*15 ints. Тогда все эти int будут жить в одном красивом большом массиве, который удобен для кэширования и требует только одного выделения и одного освобождения! Вместо, вроде 165, каждого. Теперь вам придется обернуть свои доступы некоторыми математическими выражениями (например, index = y*width + x), но это тривиально.

поэтому, если мы используем std::string по значению, а не по ссылке, есть ли у него дополнительные накладные расходы на копирование при вызове конструктора?

user542598 13.12.2020 02:11

@user542598 user542598 Для вызова нет, потому что по умолчанию он будет перемещен из временных файлов.

Asteroids With Wings 13.12.2020 21:18

да, я знаю, что std::move() не будет копировать объект, но я думаю, что в С++ передача параметра функции по значению (а не по ссылке) вызовет копирование? так что TreeNode(std::string value) вызовет копирование, а TreeNode(const std::string& value) нет? или вы говорите, что первый не вызовет копию, даже переданную по значению?

user542598 15.12.2020 02:58

@ user542598 По умолчанию он будет перемещен из временных файлов. Вот как работает семантика перемещения!

Asteroids With Wings 15.12.2020 13:35

Другие вопросы по теме