Помощник по построению make_XYZ, разрешающий RVO и вывод типа, даже если XZY имеет ограничение на отсутствие копирования
ОБНОВЛЕНИЕ1: C++17 добавил вывод типов для конструкторов, что не означает, что свободная функция является худшим решением.
ОБНОВЛЕНИЕ2: 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;
}
2 ответа
Если существует неявный конструктор, действительно возможно вернуть не копируемый и неподвижный тип по значению. Смотрите живой пример: /questions/18124260/poluchenie-subclipse-v-aptana-dlya-rabotyi-s-novejshim-vyipuskom-subversion/18124279#18124279.
template <typename T>
XYZ<T> make_XYZ (T && i)
{
return { std::forward<T>(i) };
}
Хитрый момент здесь в том, что { ... }
не создает временное и не перемещает его в возвращаемое значение. Он напрямую инициализирует возвращаемое значение. Нет ни копирования, ни перемещения, и это не имеет значения, применима ли какая-либо оптимизация (она не будет компилироваться, если для работы потребуется оптимизация).
Однако, поскольку тип не является ни копируемым, ни подвижным, вы не сможете сохранить его в локальной переменной по значению. Однако вы можете использовать старый трюк временного продления жизни, чтобы удержать его:
auto&& x = make_XYZ(1);
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');
}
К сожалению, вы должны повторить свои аргументы, но вы можете использовать макрос, чтобы обойти это (и макрос по крайней мере гарантированно безопасен, так как ни один аргумент не оценивается более одного раза).