C ++: const не допускается в универсальной (пересылочной) ссылке - PullRequest
0 голосов
/ 03 июля 2018

Я читал этот ответ стекопотока , где указана одна причина, по которой const T&& не является универсальной (переадресационной) ссылкой:

Если const T && вести себя как переадресация ссылок, это сделает невозможной перегрузку функции шаблона, которая принимает в качестве параметра только ссылку на rvalue.

Я не знаю, что это значит. Я предполагаю, что это подразумевает наличие двух перегрузок одной и той же функции (шаблона), и одна из них принимает в качестве параметра const T&&. Я также предполагаю, что одна из этих перегрузок будет вызываться всегда, а другая никогда не будет вызываться.

Если мои предположения верны, каковы две перегруженные функции? Или, если я не прав, что на самом деле означает цитируемый абзац?

Спасибо.

1 Ответ

0 голосов
/ 03 июля 2018

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

Во-первых, важно уточнить, что rvalue reference и forwarding reference - это не одно и то же, они просто имеют одинаковые обозначения &&. Является ли это хорошей вещью, подлежит обсуждению.

template <typename T>
void foo(T&&); // deduced type == forwarding reference

void foo(int&&); // explicit type == rvalue reference

Достаточно просто. Так почему же следующая ссылка для пересылки ?

template <typename T>
void foo(const T&&); // const rvalue reference despite deduced type

Лучший ответ, который я могу вам дать, - «потому что». Похоже, это совершенно произвольное решение Комитета по стандартам. Я не вижу причин, по которым const T&& не может быть ссылкой для пересылки ; это просто не потому, что так говорится в стандарте.

§14.8.2.1 / Вывод аргументов шаблона из вызова функции [temp.deduct.call]

Ссылка для пересылки - это rvalue-ссылка на cv-unqualified параметр шаблона.

Независимо от , почему это так, становится очевидным, что добавление cv-qualification является единственным способом сказать компилятору, что трактуемый тип следует рассматривать как ссылку на значение , а не как ссылка на пересылку . На что указывает ваша цитата из другого ответа.

Если const T && будет вести себя как пересылка ссылок, это сделает невозможным перегрузку функции шаблона, которая принимает в качестве параметра только ссылку на rvalue.

Причина, по которой я говорю, что это вводит в заблуждение, заключается в том, что если мы перегрузим шаблон для принятия const T&&, тогда эта перегрузка будет предпочтительнее для всех значений rvalue независимо от cv -квалификация. Это не тот случай.

В следующем коде мы видим, что foo принимает const rvalue ссылки , но ничего больше, потому что это не ссылка на пересылку .

struct Non_POD
{
    Non_POD(int i) : m_i(i) { }
    int m_i;
};

Non_POD foo() { return {0}; }

const Non_POD const_foo() { return {0}; }

template <typename T>
void bar(const T&& val)
{
    std::cout << "Accepts: const rvalue ref. ";
    if constexpr (std::is_rvalue_reference_v<decltype(val)>)
    {
        std::cout << "Val is rvalue reference.\n";
    }
    else if constexpr (std::is_lvalue_reference_v<decltype(val)>)
    {
        std::cout << "Val is lvalue reference.\n";
    }
    else
    {
        std::cout << "Val is lvalue.\n";
    }

    std::cout << std::endl;
}

int main()
{
    bar(foo());
    bar(const_foo());
    Non_POD x(0);
    //bar(x); // error
}

Ожидаемый выход (GCC 7.1)

Принимает: const rvalue ref. Val является rvalue ссылкой.

Принимает: const rvalue ref. Val является rvalue ссылкой.

Кажется, это поддерживает кавычку, потому что она принимает ссылки на постоянные значения и преобразует ссылки на значения в ссылки на постоянные значения . Однако перегрузок не происходит. Если мы введем перегрузку, то увидим, что она принимает только const rvalue ссылки .

struct Non_POD
{
    Non_POD(int i) : m_i(i) { }
    int m_i;
};

Non_POD foo() { return {0}; }

const Non_POD const_foo() { return {0}; }

template <typename T>
void bar(const T&& val)
{
    std::cout << "Accepts: const rvalue ref. ";
    if constexpr (std::is_rvalue_reference_v<decltype(val)>)
    {
        std::cout << "Val is rvalue reference.\n";
    }
    else if constexpr (std::is_lvalue_reference_v<decltype(val)>)
    {
        std::cout << "Val is lvalue reference.\n";
    }
    else
    {
        std::cout << "Val is lvalue.\n";
    }

    std::cout << std::endl;
}

template <typename T>
void bar(T&& val)
{
    std::cout << "Accepts: forwarding ref. ";
    if constexpr (std::is_rvalue_reference_v<decltype(val)>)
    {
        std::cout << "Val is rvalue reference.\n";
    }
    else if constexpr (std::is_lvalue_reference_v<decltype(val)>)
    {
        std::cout << "Val is lvalue reference.\n";
    }
    else
    {
        std::cout << "Val is lvalue.\n";
    }

    std::cout << std::endl;
}

int main()
{
    Non_POD x(0);
    const Non_POD cx(0);

    bar(x);
    bar(cx);
    bar(Non_POD(0));
    bar(foo());
    bar(const_foo());
}

Ожидаемый результат (GCC 7.1)

Принимает: пересылка исх. Val - это значение lvalue.

Принимает: пересылка исх. Val - это значение lvalue.

Принимает: пересылка исх. Val является rvalue ссылкой.

Принимает: пересылка исх. Val является rvalue ссылкой.

Принимает: const rvalue ref. Val является rvalue ссылкой.

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

...