Можно ли пропустить параметр шаблона в `std :: forward`? - PullRequest
1 голос
/ 19 апреля 2020

Стандартная подпись std::forward:

template<typename T>
constexpr T&& forward(std::remove_reference_t<T>&) noexcept;
template<typename T>
constexpr T&& forward(std::remove_reference_t<T>&&) noexcept;

Поскольку тип параметра не T напрямую, мы должны указать аргумент шаблона при использовании std::forward:

template<typename... Args>
void foo(Args&&... args)
{
    bar(std::forward<Args>(args)...);
}

Однако иногда аргумент шаблона не так прост, как Args. auto&& - это случай:

auto&& vec = foo();
bar(std::forward<decltype(vec)>(vec));

Вы также можете представить более сложные типы аргументов шаблона для std::forward. В любом случае, интуитивно std::forward должен знать, что такое T, но на самом деле это не так.

Так что моя идея состоит в том, чтобы опустить <Args> и <decltype(vec)> независимо от того, насколько они просты. Вот моя реализация:

#include <type_traits>

template<typename T>
std::add_rvalue_reference_t<std::enable_if_t<!std::is_lvalue_reference<T>::value, T>>
my_forward(T&& obj)
{
    return std::move(obj);
}

template<typename T>
T& my_forward(T& obj)
{
    return obj;
}

int main()
{
    my_forward(1); // my_forward(int&&)
    int i = 2;
    my_forward(i); // my_forward(int&)
    const int j = 3;
    my_forward(j); // my_forward(const int&)
}

Когда obj является rvalue ссылкой, например int&&, выбрана первая перегрузка, потому что T равен int, чей is_lvalue_reference равен false;

Когда obj является ссылкой на lvalue, например const int&, выбирается вторая перегрузка, потому что T равен const int&, а первая отключена SFINAE.

Если моя реализация возможно, почему std::forward все еще требует <T>? (Так что мое должно быть неосуществимо.)

Если нет, то что не так? И все же вопрос, можно ли пропустить параметр шаблона в std::forward?

Ответы [ 2 ]

3 голосов
/ 19 апреля 2020

Проблемный c случай - когда вы передаете что-то со ссылочным типом rvalue, но не принадлежащее категории значений rvalue:

int && ir{::std::move(i)};
my_forward(ir); // my_forward(int&)

Передача типа в std::forward гарантирует, что аргументы rvalue ссылочные типы будут перемещены далее как значения.

1 голос
/ 20 апреля 2020

Ответ пользователя7860670 дает пример для случая, когда это не работает. Вот причина, по которой здесь всегда нужен явный параметр шаблона.

Посмотрев на значение ссылки для пересылки, вы уже не сможете надежно определить с помощью разрешения перегрузки, безопасно ли перемещаться из. При передаче ссылочного параметра lvalue в качестве аргумента для вызова вложенной функции он будет обрабатываться как lvalue. В частности, он не будет связываться как аргумент rvalue, для которого снова потребуется явный std::move. Эта любопытная асимметрия - это то, что нарушает неявную пересылку.

Единственный способ решить, следует ли перемещать аргумент вперед, - это проверка его исходного типа. Но вызываемая функция не может сделать это неявно, поэтому мы должны явно передать выведенный тип в качестве параметра шаблона. Только проверяя этот тип напрямую, мы можем определить, хотим ли мы перейти к этому аргументу или нет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...