Я только начал работать с c ++ 11 r-значениями. Я прочитал несколько уроков, но я не нашел ответа.
Каков наилучший (самый эффективный) способ установить переменную класса? Код ниже верен или нет? (предположим, что std :: string имеет определенный конструктор перемещения и оператор присваивания).
class StringWrapper
{
private:
std::string str_;
public:
StringWrapper() : str_("") {}
void setString1(std::string&& str) {
str_ = std::move(str);
}
void setString2(const std::string& str) {
str_ = std::move(str);
}
// other possibility?
};
int main() {
std::string myText("text");
StringWrapper x1, x2;
x1.setString?("text"); // I guess here should be setString1
x2.setString?(myText); // I guess here should be setString2
}
Я знаю, что компилятор может оптимизировать мой код и / или я могу использовать функции перегрузки. Я хотел бы знать только, как лучше.
Совет Херба Саттера на этом стоит начать со стандартного подхода C ++ 98:
void setString(const std::string& str) {
str_ = str;
}
И если вам нужно оптимизировать для rvalue, добавьте перегрузку, которая принимает ссылку на rvalue:
void setString(std::string&& str) noexcept {
str_ = std::move(str);
}
Обратите внимание, что большинство реализаций std::string
используйте оптимизацию небольших строк, чтобы, если ваши строки были маленькими, то ход был таким же, как и копия, и вы не получили бы никакой выгоды.
Соблазнительно использовать передачу по значению, а затем двигаться (как в ответе Адама Хуньяди), чтобы избежать необходимости писать несколько перегрузок. Но Херб указал, что он не использует повторно какой-либо существующий потенциал str_
, Если вы вызываете его несколько раз с помощью lvalues, он будет каждый раз выделять новую строку. Если у тебя есть const std::string&
перегрузка, то он может повторно использовать существующую емкость и избежать выделения ресурсов.
Если вы действительно умны, вы можете использовать шаблонный сеттер, который использует идеальную пересылку, но получить его полностью правильно на самом деле довольно сложно.
Дизайнеры компиляторов умные люди. Используйте кристально чистый и, следовательно, ремонтопригодный
void setString(const std::string& str) {
str_ = str;
}
и пусть компилятор беспокоится об оптимизации. Довольно, пожалуйста, с сахаром сверху.
Еще лучше, не маскарадный код как инкапсулируемый. Если вы намереваетесь предоставить такой метод, то почему бы просто не сделать str_
public
? (Если вы не собираетесь вносить другие изменения в свой объект, если член меняется.)
Наконец, почему вам не нравится конструктор по умолчанию std::string
? канава str_("")
,
Версия с rvalue
ссылка обычно не будет привязана к lvalue
(в твоем случае, mytext
), вам придется переместить его, и, следовательно, построить объект дважды, оставив вас с опасным объектом. const lvalue reference
должен быть медленнее при построении из rvalue, потому что он будет делать то же самое снова: construct -> move -> move construct
,
Хотя компилятор может оптимизировать накладные расходы.
Ваша лучшая ставка на самом деле будет:
void setString(std::string str)
{
str_ = std::move(str);
}
Здесь удивительно гарантировано, что компилятор выведет тип аргумента и вызовет конструктор копирования для lvalues и конструктор перемещения для rvalues.
Обновить:
Крис Дью отметил, что создание и перемещение назначения строки на самом деле дороже, чем копирование. Теперь я убежден, что с помощью const&
аргумент является лучшим вариантом. : D
Вы могли бы, вероятно, использовать Templatized setString
и пересылка ссылок:
class StringWrapper
{
private:
std::string str_;
public:
template<typename T>
void setString(T&& str) {
str_ = std::forward<T>(str);
}
};