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

С++ 11: абстрагирование ссылок на константу, volatile, lvalue и rvalue для квалифицированных указателей на функцию-член?

С++ 03 позволяет вам квалифицировать параметры функции как const, volatile и/или lvalue ссылки (&).

С++ 11 добавляет еще одно: ссылки rvalue (&&).

Кроме того, С++ позволяет перегружать функции на основе определителей их параметров, так что при вызове функции выбирается наиболее подходящая перегрузка.

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

struct Foo
{
    int& data();             // return a non-const reference if `this` is non-const
    const int& data() const; // return a const reference if `this` is const
};

В С++ 03 возможны следующие классификаторы const и volatile, а С++ 11 также допускает, чтобы & и && (& теоретически разрешались в С++ 03, но это не было).

Любая комбинация квалификаторов может быть использована, за исключением того, что & и && являются взаимоисключающими, что делает 2 ^ 2 = 4 возможности в С++ 03 и 2 ^ 4-4 = 12 в С++ 11.

Это может быть очень больно, когда вы хотите работать с указателями функций-членов, потому что они не являются даже немного полиморфными в этих классификаторах: квалификаторы в "this type" указателя функции-члена передаются как аргумент должен точно совпадать с аргументом типа передаваемого параметра. С++ также не предлагает явного средства для абстрагирования от квалификаторов. В С++ 03 это было в основном ОК, потому что вам нужно было бы написать версию const и версию const, и никто не заботится о volatile, а в патологическом случае на С++ 11 (который не так необычно, как это патологично), вы могли бы вручную записать целых 12 перегрузок. За функцию.

Я был очень рад узнать, что если вы передаете тип охватывающего класса в качестве параметра шаблона и выводите из него тип указателя функции-члена, то квалификаторы const и volatile допускаются и распространяются как вы ожидаете:

template<typename Object>
struct Bar
{
    typedef int (Object::*Sig)(int);
};

Bar<Baz>;                // Sig will be `int (Baz::*)(int)`
Bar<const Baz>;          // Sig will be `int (Baz::*)(int) const`
Bar<volatile Baz>;       // Sig will be `int (Baz::*)(int) volatile`
Bar<const volatile Baz>; // Sig will be `int (Baz::*)(int) const volatile`

Это намного лучше, чем записывать все случаи вручную.

К сожалению, он не работает для & и &&.

GCC 4.7 говорит:

ошибка: формирование указателя на ссылочный тип "Baz &&

Но это не удивительно, учитывая, что GCC с 4.7 еще не имеет поддержки ссылочных квалификаторов на this.

Я также попробовал его с Clang 3.0, который имеет такую ​​поддержку:

error: указатель-член ссылается на неклассовый тип 'Baz &'

О, хорошо.

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

(Стоит отметить, что если С++ не различал функции-члены и обычные функции, все это было бы тривиально: вы использовали бы параметр шаблона как тип параметра функции (указатель) и аргумент шаблона будет передан через as-is, квалификаторы нетронуты, нет необходимости в дополнительных соображениях.)

4b9b3361

Ответ 1

Вы задумывались о том, чтобы просто специализировать свой шаблон?

Вы можете просто добавить две версии:

template <typename Object>
struct Bar<Object&> {
  typedef int (Object::*Sig)(int)&;
};

template <typename Object>
struct Bar<Object&&> {
  typedef int (Object::*Sig)(int)&&;
};

И тогда компилятор правильно подберет правильную специализацию (или резервную копию в общем случае).

Это избавит вас от объекта const/volatile, но подразумевает, что вам нужно написать код 3 раза.