Этот вопрос в основном является последствием этот ответ я дал. Я только что понял, что формулировка в стандарте, кажется, опускает несколько случаев. Рассмотрим этот кусок кода:
#include <iostream>
#include <functional>
struct foo
{
void f(int v) { std::cout << v << std::endl; }
};
struct bar : foo {};
int main()
{
bar obj;
std::bind(&foo::f, obj, 1)();
}
Стандарт описывает в 20.8.9.1.2 эффект std::bind
и что происходит, когда он вызывается. Переходит к 20.8.2, соответствующая часть:
20.8.2 Требования [func.require]
1 определять ВЫЗОВ
(f, t1, t2, ..., tN)
следующее:—
(t1.*f)(t2, ..., tN)
когдаf
указатель на функцию-член классаT
а такжеt1
является объектом типаT
или ссылка на объект типаT
или ссылка на объект типа, производного отT
;—
((*t1).*f)(t2, ..., tN)
когдаf
указатель на функцию-член классаT
а такжеt1
не является одним из типов, описанных в предыдущем пункте;—
t1.*f
когдаN == 1
а такжеf
указатель на данные члена классаT
а такжеt1
является объектом типаT
или ссылка на объект типаT
или ссылка на объект типа, производного отT
;—
(*t1).*f
когдаN == 1
а такжеf
указатель на данные члена классаT
а такжеt1
не является одним из типов, описанных в предыдущем пункте;—
f(t1, t2, ..., tN)
во всех остальных случаях.
Читая это, кажется, что можно разрешить три случая для первого элемента списка:
t1
является объектом типа T
T
T
но в моем примере это не так. Это тип, полученный из T
, но без ссылки. Не должны ли перечисленные выше случаи быть:
t1
является объектом типа T
T
T
T
Конечно, то же самое относится и к третьему пункту списка в 20.8.2.
Вопрос 1: Поскольку и GCC, и Clang принимают мой код, мне интересно, является ли это отчет о дефекте по сравнению со стандартом или я просто читаю его неправильно.
Вопрос 2: С множественным / виртуальным наследованием, даже если тип является производным от T
, это может быть не просто возможно позвонить (t1.*f)(...)
на этом, верно? Это также то, что меня должно беспокоить, или стандарт четко определяет «производные» в данном контексте?
Пункт 20.8.9.1.3 Стандарта C ++ 11 определяет эффекты вызова std::bind()
с точки зрения INVOKE()
псевдо-функции сюда:
Возвращает: Упаковщик переадресации
g
со слабым типом результата (20.8.2). Эффектg(u1, u2, ..., uM)
должен бытьINVOKE (fd, std::forward<V1>(v1), std::forward<V2>(v2), ..., std::forward<VN>(vN),
result_of<FD cv & (V1, V2, ..., VN)>::type)
Обратите внимание, что призыв к std::forward<>
всегда будет возвращать тип ссылки, будь то ссылка lvalue или ссылка rvalue. Согласно пунктам 20.2.3 / 1-2, фактически:
template <class T> constexpr T&& forward(typename remove_reference<T>::type& t) noexcept;
template <class T> constexpr T&& forward(typename remove_reference<T>::type&& t) noexcept;
Возвращает:
static_cast<T&&>(t)
Следовательно INVOKE
псевдо-функция (будьте осторожны: не bind()
функция!) будет вызываться здесь со ссылками, и это подтверждается приведенным вами определением.
Я считаю, что сам Стандарт никогда не использует INVOKE()
псевдофункция (это просто некоторый внутренний формализм) с объектами (не ссылками на объект) типа, производного от T
,
Однако это не означает, что вы не можете вызвать std::bind()
или другие функции, определение которых в свою очередь дается в терминах INVOKE()
с объектом (не ссылкой на объект) типа, полученного из T
,
Других решений пока нет …