Как написать постоянную ссылку на функцию

0

На данный момент у меня есть класс, подобный этому:

class dummy{
    public:
        dummy(void(&func)(int))
            : member{func}{}

        void(&member)(int);
};

но я хочу, чтобы member определен как ссылка на функцию const. Я не уверен, как это написать или если это возможно.

PS ПОЖАЛУЙСТА, не рекомендую мне std::function Я не забываю о существовании и не возражаю против этого, я просто хочу знать, возможно ли что-то подобное.

  • 0
    [dcl.fct] / 6 "Эффект cv-qualifier-seq в деклараторе функции - это не то же самое, что добавление cv-qualification поверх типа функции. В последнем случае cv-qualifiers игнорируются. [ Примечание: тип функции, имеющий cv-qualifier-seq , не является cv-квалифицированным типом; нет никаких cv-квалифицированных типов функций. - конец примечание ] "
  • 0
    определение типа во? .......
Показать ещё 1 комментарий
Теги:
c++11
function-pointers

1 ответ

2
Лучший ответ

Синтаксис:

Синтаксически вы можете достичь этого с помощью псевдонима типа (или typedef):

using function_t = void(int); // or typedef void (function_t)(int);

class dummy {
public:
    const function_t& member;
    dummy(const function_t& func)
         : member{func}{}

};

Семантика:

Тем не менее, "правильный синтаксис" не купит вам ничего: dummy - это то же самое, что и

class dummy {
public:
    function_t& member;
    dummy(function_t& func)
         : member{func}{}

};

(что, в свою очередь, совпадает с определением OP), поскольку квалификаторы const игнорируются в соответствии с C++ 11 8.3.5/6:

Эффект cv-qualifier-seq в объявлении функции не совпадает с добавлением cv-квалификации поверх типа функции. В последнем случае cv-квалификаторы игнорируются. [Примечание: тип функции, имеющий cv-qualifier-seq, не является cv-квалифицированным типом; не существует специальных типов функций cv. -End note]

Как насчет rvalues?

Насколько я undestand (из комментария к user657267 ответ) мотивацию для принятия аргумент в качестве ссылки на const было включить прохождение rvalues (временные переменные ):

void f(int) { }

function_t make_function() { return f; }

dummy d1(f);
dummy d2(make_function());

Однако это не компилируется не из-за dummy, а потому, что make_function возвращает функцию, которая запрещена C++ 11 8.3.5/8

Если тип параметра включает тип формы "указатель на массив неизвестной границы T " или "ссылка на массив неизвестной границы T ", программа плохо сформирована. 99Функции не должны иметь тип возвращаемого типа массива или функции, хотя они могут иметь тип возвращаемого типа указателя типа или ссылки на такие вещи. Массивов функций не должно быть, хотя могут быть массивы указателей на функции.

Естественное "решение" будет возвращать ссылку:

function& make_function() { return f; }

или

function&& make_function() { return f; }

В обоих случаях тип выражения make_function() является значением lvalue (которое не соответствует цели dummy использования с использованием ссылки на const чтобы разрешать передачу rvalues) согласно C++ 11 5.2.2/10

Вызов функции - это значение lvalue, если тип результата является ссылочным типом lvalue или ссылкой rvalue для типа функции, значением xvalue, если тип результата является ссылкой rvalue на тип объекта и в противном случае значением prvalue.

На самом деле, категория стоимости здесь не проблема. С любым юридическим объявлением make_function и любым определением dummy похоже, выше объявления d1 и d2 работают нормально.

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

dummy d3([](int){}); // Error!

Предлагаемое решение

Вместо ссылок используйте указатели:

using function_t = void(int); // or typedef void (function_t)(int);

class dummy {
public:
    function_t* member;
    dummy(function_t* func)
         : member{func}{}

};

void f(int) { }

function_t& make_function() { return f; }

dummy d1(f);               // OK
dummy d2(make_function()); // OK
dummy d3([](int){});       // OK

Заключительные замечания:

  1. Опять же, объявление const function_t* member и dummy(const function_t* func) не купит вам ничего, потому что, согласно ссылкам, квалификаторы const игнорируются.

  2. Инициализация d1 и d2 работает, потому что функции неявно преобразуются в указатель на функции (см. C++ 4.3/1).

  3. Если аргумент функции имеет тип функции, компилятор меняет свой тип на указатель на функцию. Следовательно, dummy конструктор может быть объявлен как dummy(function_t func); ,

  4. Инициализация d3 работает, потому что безъядерные лямбды неявно преобразуются в указатель на функции. Это не сработало бы для лямбда с захватами.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню