Почему не `const int ci = 2; std :: forward <int>(ci); `работа и как это исправить / обойти это? - PullRequest
9 голосов
/ 27 ноября 2011

Простой вопрос, почему не работает следующее (подразумевается копия ci)?

#include <utility>

int main(){
  const int ci = 2;
  std::forward<int>(ci);
}

prog.cpp: In function 'int main() ':
prog.cpp: 6: 23: ошибка: нет соответствующей функции для вызова функции forward (const int &) *

Проблема проявилась при написании некоторых шаблонов, гдеУ меня есть простой тип держателя следующим образом.Чтобы избежать ненужных копий, я использую совершенную пересылку, где это возможно, но, похоже, это является корнем проблемы.

template<class T>
struct holder{
    T value;

    holder(T&& val)
        : value(std::forward<T>(val))
    {}
};

template<class T>
holder<T> hold(T&& val){
    // T will be deduced as int, because literal `5` is a prvalue
    // which can be bound to `int&&`
    return holder<T>(std::forward<T>(val));
}

template<class T>
void foo(holder<T> const& h)
{
    std::tuple<T> t;  // contrived, actual function takes more parameters
    std::get<0>(t) = std::forward<T>(h.value); // h.value is `const T`
}

int main(){
    foo(hold(5));
}

Если требуется дополнительная информация, пожалуйста, дайте мне знать.
Любая идея обойти эту проблему очень ценится.

1 Ответ

19 голосов
/ 27 ноября 2011

Это:

#include <utility>

int main(){
  const int ci = 2;
  std::forward<int>(ci);
}

не работает, потому что вы не можете неявно отбрасывать const. std::forward<T>(u) следует читать как:

Вперед u как T.

Вы пытаетесь сказать:

Forward an lvalue `const int` as an rvalue `int`.

, который выбрасывает const. Чтобы не выбросить const, вы можете:

#include <utility>

int main(){
  const int ci = 2;
  std::forward<const int>(ci);
}

что говорит:

Forward an lvalue `const int` as an rvalue `const int`.

В вашем коде:

template<class T>
void foo(holder<T> const& h)
{
    std::tuple<T> t;  // contrived, actual function takes more parameters
    std::get<0>(t) = std::forward<T>(h.value); // h.value is `const T`
}

квалификатор const в h влияет на выражение выбора элемента данных h.value. h.value является const значением int. Вы можете использовать forward, чтобы изменить его на const rvalue int, или вы можете использовать forward, чтобы передать его без изменений (как const lvalue int). Вы могли бы даже использовать forward, чтобы добавить volatile (хотя я не могу придумать вескую причину для этого).

В вашем примере я не вижу смысла вообще использовать forward (если вы не снимаете const с h).

    std::get<0>(t) = h.value; // h.value is `const T`

Ваш комментарий даже по-прежнему правильный.

Это сухое чтение, но N2951 показывает, что можно и что нельзя делать с forward и почему. Это было изменено N3143 непосредственно перед стандартизацией, но варианты использования и обоснование по-прежнему действительны и неизменны в окончательной формулировке N3143.

Вещи, которые вы можете сделать с forward:

  • Вы можете переслать lvalue как lvalue.
  • Вы можете переслать lvalue как значение.
  • Вы можете переслать rvalue как rvalue.
  • Вы можете переслать менее квалифицированные cv выражения в более квалифицированные cv выражения.
  • Вы можете пересылать выражения производного типа в доступный однозначный базовый тип.

Вещи, которые вы можете не делать с forward:

  • Вы не можете переслать rvalue как lvalue.
  • Вы не можете пересылать больше выражений с квалификацией cv в выражения с меньшим количеством cv.
  • Вы не можете пересылать произвольные преобразования типов (например, пересылать int как double).
...