Совместное использование реализаций для перегрузок const lvalue (const T & amp;) и rvalue (T & amp; & amp;): так же, как это делается для константных и неконстантных перегрузок.

Следующий блок кода появляется в известной книге Скотта Мейерса «Эффективный С ++» Пункт 3:

class TextBlock {
public:
...
const char& operator[](std::size_t position) const
{
...    // do bounds checking
...    // log access data
...    // verify data integrity
return text[position];
}
char& operator[](std::size_t position)
{
...    // do bounds checking
...    // log access data
...    // verify data integrity
return text[position];
}
...
private:
std::string text;
};

Автор заявляет, что в приведенной выше реализации содержание const и неconst перегрузки по сути одинаковы. Чтобы избежать дублирования кода, его можно упростить следующим образом:

class TextBlock {
public:
...
const char& operator[](std::size_t position) const    // the same as before
{
...
...
...
return text[position];
}
char& operator[](std::size_t position)        // now just calls const op[]
{
return                                    // cast away const on
const_cast<char&>(                      // op[]'s return type;
static_cast<const TextBlock&>(*this)  // add const to *this's type;
[position]                          // call const version of op[]
);
}
...
private:
std::string text;
};

Мои вопросы:

  • Когда нам понадобится перегрузка для const T& и еще один для T&&? (Вот, T может быть параметром шаблона или типом класса, так T&& может означать или не означать универсальную ссылку) Я вижу, что в стандартной библиотеке многие классы обеспечивают обе перегрузки. Примерами являются конструкторы std::pair а также std::tuple, Есть тонны перегрузок. (Хорошо, я знаю, что среди функций одна из них — конструктор копирования, а одна — конструктор перемещения.)

  • Есть ли подобный трюк, чтобы поделиться реализациями для const T& а также T&& Перегрузки? Я имею в виду, если const T&& Перегрузка возвращает объект, созданный копией, и T&& Перегрузка возвращает что-то, что было сконструировано с перемещением, после совместного использования реализации это свойство все равно должно храниться. (Так же, как вышеупомянутый трюк: const возвращается const и неconst возвращает неconstкак до, так и после реализации обмена)

Спасибо!

Две перегрузки, о которых я говорю, должны выглядеть следующим образом:

Gadget f(Widget const& w);
Gadget f(Widget&& w);

Это не связано с возвратом по ссылкам rvalue, то есть:

Widget&& g(/* ... */);

(Кстати, этот вопрос был рассмотрен в мой предыдущий пост)

В f() выше, если Gadget является и конструктивно копируемым, и конструктивно перемещаемым, нет никакого способа (кроме чтения реализации), чтобы сказать, является ли возвращаемое значение созданным копированием или созданным перемещением. Нет ничего общего с оптимизацией возвращаемого значения (RVO) / оптимизацией именованных возвращаемых значений (NRVO). (Увидеть мой предыдущий пост)

Эффективный C ++

станд :: пары :: пара

станд :: кортеж :: кортеж

Когда лучше возвращаться по ссылкам?

2

Решение

• Когда нам понадобится перегрузка для const T& и еще один для Т&&?

По сути, когда перемещение дает вам прирост производительности, должен быть и конструктор перемещения. То же самое относится и к функциям, в которых в противном случае вам понадобилась бы дорогая копия.

В вашем примере, где вы возвращаете ссылку на charОднако не рекомендуется также устанавливать функцию, которая возвращается Rvalue ссылка. Скорее, возвращайте по значению и полагайтесь на способность компилятора применять RVO (см., Например, Вот)

• Есть ли похожий трюк, чтобы поделиться реализациями для const T& и т&& Перегрузки?

Я часто находил полезным установить конструктор или функцию, используя универсальную ссылку (я ленивый), то есть что-то вроде

struct MyClass
{
template<typename T /*, here possibly use SFINAE to allow only for certain types */>
MyClass(T&& _t) : t(std::forward<T>(_t)) {}
private:
SomeType t;
};

РЕДАКТИРОВАТЬ: По поводу вашего обновления: если у вас есть дорогая копия Widget в вашей функции fРекомендуется также обеспечить перегрузку, принимая Widget&&,

Gadget f(Widget const& w)
{
Widget temp = w;  //expensive copy
}
Gadget f(Widget&& w)
{
Widget temp = std::move(w);  //move
}

Вы можете объединить обе функции, используя такой шаблон функции

template<typename WidgetType
// possibly drop that SFINAE stuff
// (as it is already checked in the first assignment)
, typename std::enable_if<std::is_convertible<std::remove_reference_t<WidgetType>, Widget>::value> >
Gadget(WidgetType&& w)
{
Widget temp = std::forward<WidgetType>(w);
//or
std::remove_reference_t<WidgetType> temp2 = std::forward<WidgetType>(w);
}

… Я не сказал, что это лучше ;-).


РЕДАКТИРОВАТЬ 2: Смотрите также эта тема, который решает ваш вопрос гораздо более тщательно.

2

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


По вопросам рекламы [email protected]