2015-12-23 4 views
4

Я хочу, чтобы закодировать несколько рекурсивно взаимодействующим объединить функции, которые я думаю, должен иметь подписи: T&& merge_XYZ(T&& a, T&& b);Создать временный перейти к ссылке RValue

Они, как правило, используются рекурсивно с линиями, такие как:

return merge_XYZ(std::move(x), std::move(y)); 

Каждая из этих нескольких функций слияния украдет содержимое одного из входов и введет их в другой вход и вернет результат. Как правило, они будут иметь x и y, которые являются именами для ссылок на rvalue и поэтому должны быть возвращены обратно к rvalue-ссылкам на std::move (исправьте меня, если я ошибаюсь).

Но редко они имеют x и или y, которые являются ссылками на объекты, содержимое которых не должно быть украдено. Я определенно не хочу писать альтернативные не ворующие версии этих функций. Скорее, я хочу, чтобы вызывающий абонент справился с этим в этих редких случаях. Так что мой главный вопрос правильный способ сделать это состоит в том, чтобы явно ссылаться на строительство копии, такие как:

T temp = merge_QRS(T(x), T(y)); // use x and y without stealing yet 
return merge_XYZ(merge_MNO(std::move(x), std::move(y)), std::move(temp)); 

Главный вопрос: Является ли T(x) правильный путь, чтобы заставить временную копию, который будет создан в этой точке?

Другие вопросы:
Is T temp = правильный способ убедиться, что вызов merge_QRS в приведенном выше коде происходит перед вызовом merge_MNO, но в противном случае недорог вперед временный от в первый операнд merge_XYZ? Если я использовал T&& temp, то вместо этого он удерживал указатель на измененный T(x) после жизни T(x)?

Есть ли T&& правильный тип возврата (в отличие от T) для объединения многих из них вместе?

Как выше по сравнению с:

T tx = x; 
T&& temp = merge_QRS(std::move(tx), T(y)); // use x and y without stealing yet 
return merge_XYZ(merge_MNO(std::move(x), std::move(y)), std::move(temp)); 

Предполагая, что merge_QRS будет модифицировать ТХ и возвращающий ссылку RValue в том, что поведение всех определенных?

Запись на этот вопрос, возможно, помог мне понять, что я мог бы быть смешением двух ситуаций, которые не должны быть смешаны: Object вы не хотите, чтобы украсть против объектов, которые вы не хотите, чтобы украсть у еще. Является ли мой оригинал merge_QRS(T(y), T(x)) прав (только если он используется в одном выражении) для объектов, из которых я не хочу воровать? Но в случае, если я пытался в качестве примера я должен иметь следующее:

T tx = x; // Make copies which can be stolen from 
T ty = y; 
return merge_XYZ(merge_MNO(std::move(x), std::move(y)), 
        merge_QRS(std::move(tx), std::move(ty))); 

Я думаю, что все еще может быть смущен о краже содержимого против кражи личности. Если я вернусь на T&&, я украл личность одного входа в дополнение к краже содержимого другого. Когда мне удастся украсть личность? Если я вернусь на T, я никогда не украду личность, а иногда не смогу украсть личность неэффективно.

+1

Если вы не хотите, чтобы объект был «украден из» при передаче в качестве параметра либо не передавал его в качестве параметра, либо полагался на то, что lvalues ​​не привязываются к rvalue-ссылкам ([пример] (http : //coliru.stacked-crooked.com/a/e05851d887896a53)) –

+1

@MarcoA. Мне не нужна ошибка времени компиляции. Я хочу правильно работать с кодом. (На самом деле, мне нужна ошибка времени компиляции, когда я неправильно передаю объект функции кражи. Я думаю, что мой дизайн охватывает это. Но я хочу, чтобы был правильный способ передать их, что означает способ сделать копию с целью иметь копия украдена из). – JSF

+1

Как насчет определения 'T && merge_XYZ (T a, T b)'? Будет ли это работать? Без move() это создало бы temp, и с move() он вызывал && версию. – Arkadiy

ответ

4

Главный вопрос: Правильно ли установлен т (х) способ создания временной копии в этой точке?

Да

Т температура = правильный способ убедиться, что вызов merge_QRS в приведенном выше коде происходит перед вызовом merge_MNO, но в противном случае недорог вперед временный от в первый операнд merge_XYZ?

Да

Если бы я использовал T & & темп вместо это в конечном итоге держит указатель на модифицированном Т (х) после жизни Т (х)?

Да. Это болтливая ссылка, которую, к сожалению, компилятор не поймает.

Т & & правильный тип возврата (в отличие от Т) для построения цепочки много их вместе?

Чтобы быть честным, это не пахнет хорошо для меня.

Вы можете пересмотреть свою модель данных, чтобы быть чем-то более стандартным, т.е .:

T merge(T x, T y) 
{ 
    // do some merging 
    return x; 
} 

Copy-элизия и РВО позволит устранить избыточные копии. Теперь вы можете перемещать элементы, передавать копии или передавать временные файлы. Существует только одна часть логики для поддержки, и ваш код имеет значение-семантику ..., которая всегда лучше (TM).

+0

Я почти готов поверить, что ваш ответ в сочетании с комментарием. Аркадически сделанный в сочетании с еще одной деталью (возможно, очевидной) выполним: переходите и возвращайтесь по значению. Но обычно переходите к 'std :: move'. Доверяйте комбинацию «T :: T (T &&)» и скопируйте elision для решения случаев (рекурсивная пересылка), что я уверен, что копия elision сама по себе не может решить. Возможно, мне придется протестировать некоторый оптимизированный фиктивный код с побочными эффектами 'cout', которые должны быть удалены, чтобы обернуть вокруг себя голову. – JSF

+0

Но после немного более задумчивого, я слишком обеспокоен риском тихих случайных копий. Я предпочитаю дизайн, в котором конструктор копирования никогда не вызывается, если вы не планируете его использовать. Я понимаю, что торгуется против риска оборванных ссылок. Я еще раз подумаю. – JSF

+0

@JSF Я получаю смущающее чувство, что вы идете против современного состояния. В двух словах, чего вы действительно пытаетесь достичь? –

0

ТЛ; др: с семантикой значений вы получите:

  • движется происходит, где это возможно
  • Нет необходимости в Явно копировать аргументы
  • нет оборванных ссылок в случае прохождения prvalues ​​функции

Рассмотрим

struct X 
{ 
    X() = default; 
    X(int y) : a(y) {} 
    X(X &&r) : a(r.a) { std::cout << "move X..."; } 
    X(X const &r) : a(r.a) { std::cout << "copy X..."; } 
    int a; 
}; 

с foo:

X&& foo(X &&a) { return std::move(a); } 

и и bar:

X bar(X a) { return a; } 

Тогда при выполнении следующего кода:

std::cout << "foo:\n"; 
X x{ 55 }; 
std::cout << "a: "; 
X && a = foo(std::move(x)); // fine 
std::cout << "\nb: "; 
X && b = foo(X(x)); // !! dangling RV is not prvalue but xvalue 
std::cout << "\nc: "; 
X c = foo(std::move(x)); // fine, copy 
std::cout << "\nd: "; 
X d = foo(X(x)); // fine 
std::cout << "\ne: "; 
X && e = foo(X{ 12 }); // !! dangling... 
std::cout << "\nf: "; 
X f = foo(X{ 12 }); // fine 

std::cout << "\n\nbar:\n"; 
X y{ 55 }; 
std::cout << "a: "; 
X && q = bar(std::move(y)); // fine 
std::cout << "\nb: "; 
X && r = bar(y); // no explict copy required, supported by syntax 
std::cout << "\nc: "; 
X s = bar(std::move(y)); // fine 
std::cout << "\nd: "; 
X t = bar(y); // fine, no explict copy required either 
std::cout << "\ne: "; 
X && u = bar(X{ 12 }); // fine 
std::cout << "\nf: "; 
X v = bar(X{ 12 }); // fine 
std::cout << "\n"; 

получаем

foo: 
a: 
b: copy X... 
c: move X... 
d: copy X...move X... 
e: 
f: move X... 

bar : 
a: move X...move X... 
b: copy X...move X... 
c: move X...move X... 
d: copy X...move X... 
e: move X... 
f: move X... 

по VS 2015 и g ++ 5.2.

Таким образом, единственные экземпляры, сделанные (с bar), находятся в случаях b и d, что является желательным поведением в любом случае, но вы избавляетесь от возможных оборванных ссылок за 1-2 ходов за операцию (что может быть даже афайком в некоторых случаях также можно оптимизировать).

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