c ++ 11 — Каков наиболее эффективный способ установить переменную класса с помощью rvalue в c ++?

Я только начал работать с 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
}

Я знаю, что компилятор может оптимизировать мой код и / или я могу использовать функции перегрузки. Я хотел бы знать только, как лучше.

3

Решение

Совет Херба Саттера на этом стоит начать со стандартного подхода 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& перегрузка, то он может повторно использовать существующую емкость и избежать выделения ресурсов.

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

4

Другие решения

Дизайнеры компиляторов умные люди. Используйте кристально чистый и, следовательно, ремонтопригодный

void setString(const std::string& str) {
str_ = str;
}

и пусть компилятор беспокоится об оптимизации. Довольно, пожалуйста, с сахаром сверху.

Еще лучше, не маскарадный код как инкапсулируемый. Если вы намереваетесь предоставить такой метод, то почему бы просто не сделать str_ public? (Если вы не собираетесь вносить другие изменения в свой объект, если член меняется.)

Наконец, почему вам не нравится конструктор по умолчанию std::string? канава str_(""),

4

Версия с rvalue ссылка обычно не будет привязана к lvalue (в твоем случае, mytext), вам придется переместить его, и, следовательно, построить объект дважды, оставив вас с опасным объектом. const lvalue reference должен быть медленнее при построении из rvalue, потому что он будет делать то же самое снова: construct -> move -> move construct,

Хотя компилятор может оптимизировать накладные расходы.

Ваша лучшая ставка на самом деле будет:

void setString(std::string str)
{
str_ = std::move(str);
}

Здесь удивительно гарантировано, что компилятор выведет тип аргумента и вызовет конструктор копирования для lvalues ​​и конструктор перемещения для rvalues.

Обновить:

Крис Дью отметил, что создание и перемещение назначения строки на самом деле дороже, чем копирование. Теперь я убежден, что с помощью const& аргумент является лучшим вариантом. : D

4

Вы могли бы, вероятно, использовать Templatized setString и пересылка ссылок:

class StringWrapper
{
private:
std::string str_;

public:
template<typename T>
void setString(T&& str) {
str_ = std::forward<T>(str);
}
};
3
По вопросам рекламы [email protected]