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

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

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

Другая проблема, которая возникает при этом, - это невозможность определить эффективный cv-ref-qualifier для *this в определенном смысле. Следующий код не позволяет определить, является ли ref-qualifier функции-члена 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-qual-qual тип *this. На мой взгляд, введение такого синтаксиса в будущую версию стандарта C ++ не было бы серьезным изменением. Но && как пересылка cv-ref-qualifier (и это похоже на упущение комитета (а именно, рабочей группы основного языка)).

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

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


person Tomilov Anatoliy    schedule 30.09.2015    source источник
comment
Часть о программном определении того, является ли участник квалифицированным, например & или &&, вероятно, заслуживают отдельного вопроса. Например. на самом деле это не связано с прямыми ссылками (как раз наоборот), которые вы используете для сравнения того, что, как я считаю, является основным вопросом.   -  person Luc Danton    schedule 30.09.2015
comment
@LucDanton В приведенном выше контексте проблемы сильно взаимосвязаны.   -  person Tomilov Anatoliy    schedule 01.10.2015
comment
Я не понимаю, как это сделать. Вот попытка подытожить: «Почему пересылка ссылок допускает абстракцию по квалификаторам cv-ref, тогда как это невозможно для неявного параметра?» с одной стороны, «может или будет код обнаруживать квалификаторы cv-ref, которые применяются к аргументу неявного параметра?» с другой.   -  person Luc Danton    schedule 01.10.2015
comment
@LucDanton Если у кого-то есть единственная функция пересылки cv-ref-qualified, то код, который она составляет, должен знать, какая перегрузка выбрана во время каждого конкретного экземпляра, не так ли?   -  person Tomilov Anatoliy    schedule 01.10.2015
comment
Вы задаете третий вопрос?   -  person Luc Danton    schedule 01.10.2015
comment
@LucDanton На мой взгляд - нет. Но моя система знаний может отличаться от вашей. И мое представление о проблеме соответственно тоже.   -  person Tomilov Anatoliy    schedule 01.10.2015


Ответы (1)


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

Фон:

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

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

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

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

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

person Rumburak    schedule 31.01.2016
comment
Преимущество функции-члена перед глобальной функцией заключается не только в синтаксисе, особенно когда класс является шаблоном и вы имеете дело с перегрузками. В глобальной области действия вы должны как-то различать, что в области действия класса будет A<T>::f(T) и A<T>::f(U). - person Vahagn; 24.10.2016