2017-02-20 1 views
1

У меня есть функция библиотеки (не под моим контролем здесь), которые принимают ссылку г значение на движимое и Copyable типа Bar:запускает копию в качестве входных данных для функции принятия RValue ссылки

void foo(Bar&& b); 

В моем собственном коде я иногда нужно дать ему копию существующего значения, такие как

const auto b = getBar(); 
foo(mk_copy(b)); 
b.baz(); 

Это то, что приходит на ум,

template<typename T> auto mk_copy(T val){return val;} 

Есть ли стандартный способ сделать это, или более распространенный шаблон? Может быть, даже

template<typename T> auto mk_copy(T&& val){return std::forward<T>(val);} 

Или, как pscill указывает только писать имя класса снова,

foo(Bar(b)); 

, но я предпочитаю не повторять имена типов.

+1

Как насчет просто вызова конструктора копирования? 'foo (Bar {b});' – pschill

+1

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

+0

Почему вы не можете просто написать перегрузку 'foo' для lvalues ​​в том же пространстве имен, что и реальная функция библиотеки (которая внутренне копирует и перемещается к реальной функции), так что код вызова может выглядеть совершенно естественным без' mk_copy' шум? – ildjarn

ответ

1

Нет стандартного механизма, но если вы хотите его, ваши примеры не очень хороши. Первые mk_copy копий T дважды (или копии и ходы). Второй кажется очень запутанным в отношении того, что он пытается сделать.

Очевидный способ просто взять const T&, как вы это обычно:

template<typename T> T mk_copy(const T &val){return val;} 
+0

Не применяется ли RVO для первого? – songyuanyao

+2

@songyuanyao: Нет. [Elision не применяется к параметрам функции] (http://stackoverflow.com/questions/6009278/why-are-by-value-parameters-excluded-from-nrvo/9595610#9595610). –

+0

Спасибо. Во-первых, делает ли копия и нет? Второй должен сделать одну копию, но, если возможно, оптимизируйте ее до одного хода. Немного перебор, но не так ли? Я признаю, что не думал об этом. –

1

Для встроенных типов префикс + играет роль “ сделать копию ”:

void foo(char&&) {} 
void bar(double*&&) {} 

auto main() -> int 
{ 
    char x{}; 
    foo(+x); 

    double* p{}; 
    bar(+p); 
} 

Тем не менее, вероятно, это будет путать читателей кода, если вы примените префикс + к другому типу, а общий префикс operator+ может оказаться в c onflict с собственным префиксом класса operator+.

Итак, я предлагаю использовать теперь очевидное соглашение об именах для создателей, а именно префикс make.

Тогда он может выглядеть следующим образом:

template< class Type > 
auto make_copy(Type const& o) -> Type { return o; } 

Ваше первое предложенное решение,

template<typename T> auto mk_copy(T val){return val;} 

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

Это не проблема для подвижного типа, такого как стандартный контейнер библиотеки, потому что копирование типа возвращаемого объекта будет уменьшаться до перемещения. Но это может быть проблемой для довольно большого непереходного типа, как это может происходить в устаревшем коде.


Второе предложенное решение,

template<typename T> auto mk_copy(T&& val){return std::forward<T>(val);} 

принимает аргумент путем пересылки ссылки (а.к.а.универсальная ссылка), а затем выводит тип возврата из повторного создания типа аргумента. Тип возвращаемого значения всегда будет не ссылкой, так как это делает вывод auto, поэтому это технически корректно. Но это бесполезно сложно.

1

Я думаю, вы бы лучше просто определить, что mk_copy метод, или добавить еще один объявление:

auto b_copy = b; 
foo(std::move(b_copy)); 

Это понятнее читателям.


Просто, чтобы продемонстрировать свои возможности, вы можете использовать decltype получить тип:

foo(std::decay_t<decltype(b)>{b}); 

или вы могли бы получить тот же эффект с лямбды:

foo([&]{return b;}()); 

или кортежей:

foo(std::get<0>(std::make_tuple(b))); 

или пары:

foo(std::make_pair(b, 0).first); 
+0

decltype (b) {b} выводит ссылку, если b является ссылкой, так что это не сработает. –

+0

@JohanLundberg Добавлено 'std :: decay'. – kennytm

+0

ОК :). Кроме того, я думаю, что есть только нижние стороны с использованием {} для вызова конструктора копирования. (У класса может быть конструктор списка инициализаторов, делающий что-то совершенно другое.) –

Смежные вопросы