Как новичок в 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'
И компилятор сообщал об этой ошибке:
Просто предположение, используйте const std::string &val
в качестве параметра конструктора.
Это не имеет ничего общего с vector
. Просто TreeNode("abc");
уже не компилируется.
Похоже, в первую очередь вам не хватает const
в параметре конструктора val
: coliru.stacked-crooked.com/a/3e838a31605cdb1f
что весьма эффективно" Нужна цитата! Я всегда рекомендую против таких вложенных векторов. Это совершенно не нужно с кучей косвенных и накладных расходов.
@ChrisMM Спасибо за ответ, я думаю, что это решает вопрос. в дополнение к этому вопросу, знаете ли вы, что такое vector<int>(2, 0) или TreeNode("str") в выражении? Это временный объект или какой-то конструктор?
@ πάνταῥεῖ Я просто предполагаю, что он не присваивает значения и не вставляет векторы вручную в вектор, например vec.push(vector<int>(15, 0)
. Думал, эффективно. Мне интересно, на что похожи косвенность и накладные расходы?
@user542598 user542598 Оба они временные. Да, создание временного объекта требует вызова конструктора. Будучи педантичным, хотя многие люди скажут, что код сам по себе является «вызовом конструктора», это не так; это просто похоже на один.
Проблема не в инициализации std::vector (что правильно), а в инициализации структуры TreeNode.
Этот измененный код работает:
std::string initVal = "abc";
std::vector<TreeNode> vec(10, TreeNode(initVal));
Итак, источник проблемы?
Это потому, что VS позволяет привязывать временные объекты к lvalue refs-to-non-const
. Это не стандартный С++.
@AsteroidsWithWings Не похоже, что это связано с расширением, поскольку существует реальная переменная правильного типа для привязки к ссылке, а не к исходной временной.
@AsteroidsWithWings: работает и в других компиляторах.
@RetiredNinja Ах, я не заметил, что Габриэле предоставляет свежий код. Я прочитал ответ о том, что исходный код работает в VS. Виноват.
Тем не менее, это не лучший ответ, так как он даже не пытается объяснить, почему проблема существует и почему этот измененный код исправляет ее.
Строковый литерал вроде "abc"
не является std::string
. Но это нормально! std::string
, например тот, который является параметром в вашем конструкторе TreeNode
, может быть создан из него автоматически.
Однако эта строка будет временной, а временные строки не привязываются к ссылкам, отличным от const
(за исключением расширения Visual Studio).
Это тоже нормально: они вам не нужны; вместо этого ваш конструктор может взять const std::string&
и все будет хорошо.
Другие решения включают в себя получение std::string
по значению, что на самом деле является хорошей идеей, поскольку тогда вы можете std::move
из него внутри конструктора, и теперь ваш конструктор эффективен независимо от того, передаете ли вы временную или перемещаете какую-либо другую именованную строку. Но это история для другого дня.
Кроме того, если этот проект предназначен для производства и у вас, вероятно, будет намного больше векторов, чем показано в вашем примере, я настоятельно рекомендую вам не вкладывать их таким образом. Каждый элемент имеет собственное управление памятью и косвенное обращение, что очень расточительно. Ваши данные имеют квадратную форму (внутреннее измерение всегда имеет одинаковый размер), поэтому все, что вам действительно нужно, — это один вектор размером 10*15 int
s. Тогда все эти int
будут жить в одном красивом большом массиве, который удобен для кэширования и требует только одного выделения и одного освобождения! Вместо, вроде 165, каждого. Теперь вам придется обернуть свои доступы некоторыми математическими выражениями (например, index = y*width + x
), но это тривиально.
поэтому, если мы используем std::string по значению, а не по ссылке, есть ли у него дополнительные накладные расходы на копирование при вызове конструктора?
@user542598 user542598 Для вызова нет, потому что по умолчанию он будет перемещен из временных файлов.
да, я знаю, что std::move()
не будет копировать объект, но я думаю, что в С++ передача параметра функции по значению (а не по ссылке) вызовет копирование? так что TreeNode(std::string value)
вызовет копирование, а TreeNode(const std::string& value)
нет? или вы говорите, что первый не вызовет копию, даже переданную по значению?
@ user542598 По умолчанию он будет перемещен из временных файлов. Вот как работает семантика перемещения!
Если вы говорите, что он не будет компилироваться, вам нужно указать ошибки компилятора.