помощник по построению make_XYZ, разрешающий RVO и вывод типа, даже если XZY имеет ограничение на отсутствие копирования

Update1: В C ++ 17 добавлен вывод типов для конструкторов, что не означает, что свободная функция является худшим решением.

UPDATE2: В C ++ 17 добавлено гарантированное удаление копии (копирование даже не происходит концептуально). Так что с C ++ 17 мой код действительно работает и с оптимальной производительностью. Но код Мартиньо, использующий инициализацию фигурной скобки для возвращаемого значения, я все еще считаю более чистым решением. Но оформить заказ этот ответ от Барри и комментарий от Т.С.

СТАРЫЙ ПОСТ: Дедукция типов не работает для конструкторов (по крайней мере до и включая C ++ 11). Распространенным решением является использование RVO (Оптимизация возвращаемого значения) и написание шаблонной функции make_XYZ, которая перенаправляет свои параметры в конструктор. Примером является std::make_tuple,

Любой шаблонный акробат, который знает обходной путь, чтобы заставить это работать, когда политика nocopy мешает? Действительное решение все еще должно позволить RVO произойти.

Кроме того, исчезнет ли требование для любого make_XYZ в C ++ 14?

#include <iostream>

template <typename T>
struct XYZ
{
// remove following two lines to make the code compile
XYZ (XYZ const & rhs) = delete;
XYZ (XYZ && rhs) = delete;
T i;
XYZ (T i):i(i)
{
}
};

template <typename T>
XYZ<T> make_XYZ (T && i)
{
return XYZ<T>(std::forward<T>(i));
}

int main ()
{
auto x = make_XYZ(1);
std::cout << x.i << std::endl;
}

13

Решение

Если существует неявный конструктор, действительно возможно вернуть не копируемый и неподвижный тип по значению. Смотрите живой пример: http://coliru.stacked-crooked.com/a/89ef9d3115924558.

template <typename T>
XYZ<T> make_XYZ (T && i)
{
return { std::forward<T>(i) };
}

Хитрый момент здесь в том, что { ... } не создает временное и не перемещает его в возвращаемое значение. это прямая инициализация возвращаемое значение. Нет ни копирования, ни перемещения, и это не имеет значения, применима ли какая-либо оптимизация (она не будет компилироваться, если для работы потребуется оптимизация).

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

auto&& x = make_XYZ(1);
20

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

RVO — это только оптимизация; копия / перемещение должна быть доступна для использования при возврате объекта (временного или именованного) из функции.

Я бы предложил использовать make_XYZ только в неоцененном контексте, используя decltype:

#include <utility>

struct noncopy {
noncopy() {}
noncopy(noncopy &&) = delete;
noncopy(const noncopy &) = delete;
};

template<class T1, class T2>
struct noncopy_pair: public std::pair<T1, T2>, private noncopy {
using std::pair<T1, T2>::pair;
};

template<class T1, class T2>
noncopy_pair<T1, T2> make_noncopy_pair(T1 &&t, T2 &&u);

int main() {
auto &&x = decltype(make_noncopy_pair(1, 'c'))(1, 'c');
}

К сожалению, вы должны повторить свои аргументы, но вы можете использовать макрос, чтобы обойти это (и макрос, по крайней мере, гарантированно безопасен, так как ни один аргумент не оценивается более одного раза).

1