Подтвердить что ты не робот

Пересылка cv-ref-qualifier для функций-членов

Если нет других перегрузок (например, f(T &) или f(volatile T &&)) шаблона функции (члена) template< typename T > f(T &&);, тогда T && является так называемой ссылкой пересылки, а T является либо U, или U & для некоторого cv-квалифицированного типа U. Но для cv-ref-квалификаторов функций-членов такого правила нет. В struct S { void f() && { ; } }; a S::f() всегда имеет квалификатор rvalue-reference.

В общем коде было бы очень полезно избегать определения 4 (или даже 8, если мы также рассматриваем квалификатор volatile) перегрузки некоторой функции-члена, в случаях, если все они выполняют в целом одно и то же.

Другая проблема, возникающая таким образом, невозможна определить эффективный cv-ref-квалификатор *this в определенном смысле. Следующий код не позволяет определить, является ли ref-определитель функции-члена operator () && &.

#include <type_traits>
#include <utility>
#include <iostream>

#include <cstdlib>

#define P \
{                                                                       \
    using this_ref = decltype((*this));                                 \
    using this_type = std::remove_reference_t< this_ref >;              \
    std::cout << qual() << ' '                                          \
              << (std::is_volatile< this_type >{} ? "volatile " : "")   \
              << (std::is_const< this_type >{} ? "const " : "")         \
              << (std::is_lvalue_reference< this_ref >{} ? "&" : "&&")  \
              << std::endl;                                             \
}

struct F
{
    constexpr int qual() & { return 0; }
    constexpr int qual() const & { return 1; }
    constexpr int qual() && { return 2; }
    constexpr int qual() const && { return 3; }
    constexpr int qual() volatile & { return 4; }
    constexpr int qual() volatile const & { return 5; }
    constexpr int qual() volatile && { return 6; }
    constexpr int qual() volatile const && { return 7; }
    void operator () () & P
    void operator () () const & P
    void operator () () && P
    void operator () () const && P
    void operator () () volatile & P
    void operator () () volatile const & P
    void operator () () volatile && P
    void operator () () volatile const && P
};

int
main()
{
    {
        F v;
        F const c{};
        v();
        c();
        std::move(v)();
        std::move(c)();
    }
    {
        volatile F v;
        volatile F const c{};
        v();
        c();
        std::move(v)();
        std::move(c)();
    }
    return EXIT_SUCCESS;
}

Но было бы очень приятно, если бы был синтаксис выше. То есть decltype((*this)) обозначает точный cv-ref-квалифицированный тип *this. Это не было бы переломным введением такого синтаксиса в будущую версию стандарта С++. Но && как пересылка cv-ref-qualifier (и это похоже на упущение комитета (а именно, рабочей группы основного языка)).

Другую последовательность можно обозначить как функцию члена cv-ref-qualifier, так и cv-ref-qual-type *this в ее теле: auto &&, decltype(&&) и т.д.

Есть ли предложение по этой проблеме, подготовленное для использования в С++ 17?

4b9b3361

Ответ 1

Да, есть такие предложения.

Фон:

Поскольку у нас уже есть пересылка ссылок в функциях шаблона, вы можете просто превратить свою функцию-член в функцию друга-шаблона (и защитить ее с помощью enable_if от использования с любым другим классом, чем F, если требуется).

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

Предложения:

Посмотрите на предложения синтаксиса унифицированных вызовов, например: n4174

Если что-то подобное принято, вы сможете использовать свободные функции, такие как функции-члены первого аргумента. Это будет охватывать пример кода, который вы указали в своем первом комментарии. По общему признанию, он не будет охватывать operator(), но я считаю, что небольшая неприятность по сравнению с записью 8 перегрузок: -)