C ++ 11: Абстрагируясь от указателей на функции-члены const, volatile, lvalue и rvalue, ссылки на квалифицированные функции-члены? - PullRequest
13 голосов
/ 15 января 2012

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 ++ не различал функции-члены и обычные функции, все это было бы тривиально: вы бы использовали параметр шаблона в качестве типа параметра функции (указателя), а Аргумент шаблона будет передан как есть, квалификаторы не повреждены, дополнительные мысли не требуются.)

1 Ответ

4 голосов
/ 16 января 2012

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

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...