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

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 ++ 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 говорит:

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

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

Я также пробовал это с Clang 3.0, у которого есть такая поддержка:

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

О, хорошо.

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

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

13
задан glaebhoerl 15 January 2012 в 19:38
поделиться