C ++ 03 позволяет квалифицировать параметры функции как ссылки const
, volatile
и / или lvalue (&
).
C ++ 11 добавляет еще одну ссылку: rvalue (&&
).
Кроме того, C ++ позволяет вам перегружать функции на основе квалификаторов их параметров, так что при вызове функции выбирается наиболее подходящая перегрузка.
Функция-член концептуально может рассматриваться как функция, которая принимает дополнительный параметр, тип которого является ссылкой на экземпляр класса, членом которого она является. Можно перегрузить функцию-член на основе квалификаторов этого «дополнительного параметра» почти так же, как и любой другой параметр. Это выражается помещением квалификаторов в конце сигнатуры функции:
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
};
В C ++ 03 возможны квалификаторы const
и volatile
, и C ++ 11 также допускает &
и &&
(теоретически * C 101 03 * можно было бы разрешить &
, но это не было).
Может использоваться любая комбинация квалификаторов, за исключением того, что &
и &&
являются взаимоисключающими, что дает 2 ^ 2 = 4 возможности в C ++ 03 и 2 ^ 4-4 = 12 в C ++ 11.
Это может быть довольно болезненно, если вы хотите работать с указателями на функции-члены, потому что они даже не немного полиморфны в этих квалификаторах: квалификаторы типа "this
" указателя на функцию-член передаются как аргумент должен точно соответствовать аргументам типа передаваемого параметра. C ++ также не предлагает явных возможностей абстрагироваться от квалификаторов В C ++ 03 это было в основном нормально, потому что вам пришлось бы писать версию const
и версию, отличную от const
, и никто не заботился о volatile
, но в патологическом случае в C ++ 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 говорит:
ошибка: формирование указателя на ссылочный тип az Baz && ’
Но это не так уж и удивительно, учитывая, что в GCC по состоянию на 4.7 еще нет поддержки эталонных квалификаторов в this
.
Я также попробовал это с Clang 3.0, который имеет такую поддержку:
ошибка: указатель на член относится к не классу типа 'Baz &&'
О, хорошо.
Правильно ли я пришел к выводу, что это невозможно, и что нет способа абстрагироваться от квалификаторов ссылок на "this type
" указателей на функции-члены? Любые другие методы абстрагирования через квалификаторы (особенно на this
), кроме как в конкретном случае, когда вы передаете "this
тип" в качестве параметра шаблона, также приветствуются.
(Стоит отметить, что если бы C ++ не различал функции-члены и обычные функции, все это было бы тривиально: вы бы использовали параметр шаблона в качестве типа параметра функции (указателя), а Аргумент шаблона будет передан как есть, квалификаторы не повреждены, дополнительные мысли не требуются.)