Многократные возвращаемые значения (структурированные привязки) с неподвижными типами и гарантированным RVO в C++17

С C++ 17 у нас будет возможность возвращать неподвижные (в том числе не копируемые) типы, такие как std::mutex через то, что можно рассматривать как гарантированную оптимизацию возвращаемого значения (RVO): Гарантированное исключение копирования через упрощенные категории значений:

struct nocopy { nocopy(nocopy&) = delete; nocopy() = default; };
auto getRVO(){
    return nocopy();
}

У нас также будут структурированные привязки, позволяющие:

tuple<T1,T2,T3> f();
auto [x,y,z] = f();

или (здесь также используется мое понимание вывода аргументов шаблона объекта для конструкторов)

template<typename T1,typename T2,typename T3>
struct many {
  T1 a;
  T2 b;
  T3 c;
};
// (Original questions missed 'many' on the next line. Thanks, T.C.)
auto f(){ return many{string(),5.7, false} }; 
auto [x,y,z] = f();

Но эти функции составляют, чтобы включить что-то вроде этого?

auto get_ensured_rvo_str(){
    return std::pair(std::string(),nocopy());
}

auto get_class_and_mutex(){
    return many{SomeClass(),std::mutex(),std::string()};
}

int main(){
    auto rvoStr = get_ensured_rvo_str().first;
    auto [ mtx,sc,str ] = get_class_and_mutex();
}

Я думаю, что для того, чтобы это работало, это потребовало бы гарантированного RVO агрегатных аргументов конструктора при формировании std::tuple или же many Но разве это не будет названо RVO (NRVO), которое специально не включено в предложение P0144R2?


Примечание: P0144R2 специально упоминает, что поддерживаются только типы перемещения:

2.6 Типы только для перемещения

Поддерживаются только типы перемещения. Например:

struct S { int i; unique_ptr<widget> w; };
S f() { return {0, make_unique<widget>()}; }
auto [ my_i, my_w ] = f();

2 ответа

Решение
template<typename T1,typename T2,typename T3>
struct many {
  T1 a;
  T2 b;
  T3 c;
};
auto f(){ return {string(),5.7, false} };

Это не скомпилируется. Сначала ты никогда не говорил f это вернуть many, Во-вторых, дедукция аргументов шаблона класса работает с конструкторами и единственными many являются неявно объявленными конструкторами по умолчанию, копировать и перемещать.

Вам нужен гид:

template<class T1, class T2, class T3>
many(T1, T2, T3) -> many<T1, T2, T3>;
auto get_ensured_rvo_str(){
    return std::pair(std::string(),nocopy());
}

Это тоже не работает. nocopy() материализуется во временную, которая связана с опорным параметром pairконструктор, который затем пытается перейти от него и терпит неудачу. Исключение этого временного не возможно и не разрешено.

(Конечно, как указывает Николь Болас в своем ответе, доступ ученика в get_ensured_rvo_str().first материализует pair возвращаемое значение get_ensured_rvo_str, так rvoStr на самом деле будет перемещен построен из first член этого материализованного временного. Но здесь у вас есть проблемы задолго до этого.)

auto get_class_and_mutex(){
    return many{SomeClass(),std::mutex(),std::string()};
}
auto [ mtx,sc,str ] = get_class_and_mutex();

Это хорошо (при условии, что у вас есть руководство по удержанию). Агрегированная инициализация не вызывает конструктор many; он инициализирует элементы непосредственно с соответствующим инициализатором prvalue.

Структурированное связывание определено для работы на основе извлечения ссылок или псевдо-ссылок на отдельные значения. То есть, если вы сделаете это:

auto [x,y,z] = f();

Что вы получаете, это что-то вроде этого:

auto HIDDEN_VALUE = f();
auto &x = get<0>(HIDDEN_VALUE);
auto &y = get<1>(HIDDEN_VALUE);
auto &z = get<2>(HIDDEN_VALUE);

Когда имеешь дело со структурами, x, y, а также z не будет ссылок; они будут чем-то, что "ссылается" на фактический член массива, но это не фактическая ссылка. Главное, что x, y а также z никогда не являются копиями чего-либо.

Таким образом, вопрос заключается в том, HIDDEN_VALUE копируется. И ясно, что HIDDEN_VALUE это значение построено. И, таким образом, если возвращение f() является prvalue, то будут применяться правила гарантированного отбора.

auto rvoStr = get_ensured_rvo_str().first;

Выражение get_ensured_rvo_str() это prvalue. Тем не менее, результат применения .first это не prvalue. применение .first заставляет prvalue (в соответствии с правилами гарантированного выбора) построить временный, с .first применительно к нему. Извлеченный элемент, который является xvalue, будет использоваться для копирования инициализации rvoStr,

Таким образом, ни при какой версии стандарта копия не rvoStr опущены.

return many{SomeClass(),std::mutex(),std::string()};
...
auto [ mtx,sc,str ] = get_class_and_mutex();

Я собираюсь предположить, что вы сделали необходимые дополнения, необходимые для return заявление для компиляции.

Учитывая это, конструкция в функции будет непосредственно инициализировать HIDDEN_VALUE на обратном сайте. И каждый из элементов агрегата будет инициализирован непосредственно prvalues, поэтому копирование не будет.

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