На данный момент у меня есть класс, подобный этому:
class dummy{
public:
dummy(void(&func)(int))
: member{func}{}
void(&member)(int);
};
но я хочу, чтобы member
определен как ссылка на функцию const
. Я не уверен, как это написать или если это возможно.
PS ПОЖАЛУЙСТА, не рекомендую мне std::function
Я не забываю о существовании и не возражаю против этого, я просто хочу знать, возможно ли что-то подобное.
Синтаксис:
Синтаксически вы можете достичь этого с помощью псевдонима типа (или 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
Заключительные замечания:
Опять же, объявление const function_t* member
и dummy(const function_t* func)
не купит вам ничего, потому что, согласно ссылкам, квалификаторы const
игнорируются.
Инициализация d1
и d2
работает, потому что функции неявно преобразуются в указатель на функции (см. C++ 4.3/1).
Если аргумент функции имеет тип функции, компилятор меняет свой тип на указатель на функцию. Следовательно, dummy
конструктор может быть объявлен как dummy(function_t func);
,
Инициализация d3
работает, потому что безъядерные лямбды неявно преобразуются в указатель на функции. Это не сработало бы для лямбда с захватами.