Reputation: 56863
This question is basically an aftermath of this answer I gave. I just realized that the wording in the standard seems to omit a few cases. Consider this piece of code:
#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)();
}
The standard describes in 20.8.9.1.2 the effect of std::bind
and what happens when it is invoked. It forwards to 20.8.2, the relevant part is:
20.8.2 Requirements [func.require]
1 Define INVOKE
(f, t1, t2, ..., tN)
as follows:—
(t1.*f)(t2, ..., tN)
whenf
is a pointer to a member function of a classT
andt1
is an object of typeT
or a reference to an object of typeT
or a reference to an object of a type derived fromT
;—
((*t1).*f)(t2, ..., tN)
whenf
is a pointer to a member function of a classT
andt1
is not one of the types described in the previous item;—
t1.*f
whenN == 1
andf
is a pointer to member data of a classT
andt1
is an object of typeT
or a reference to an object of typeT
or a reference to an object of a type derived fromT
;—
(*t1).*f
whenN == 1
andf
is a pointer to member data of a classT
andt1
is not one of the types described in the previous item;—
f(t1, t2, ..., tN)
in all other cases.
Reading this, it seems to allow three cases for the first list item:
t1
is an object of type T
T
T
but in my example, it is neither. It's a type derived from T
, but without a reference. Shouldn't the above listed cases be:
t1
is an object of type T
T
T
T
Of course, the same applies to the third list item in 20.8.2.
Question 1: As both GCC and Clang accept my code, I wonder if this is a defect report against the standard or if I'm just reading it wrong.
Question 2: With multiple/virtual inheritance, even if a type is derived from T
, it might not simply be possible to call (t1.*f)(...)
on it, right? Is that also something I should be concerned about or does the standard define "derived from" clearly in the given context?
Upvotes: 4
Views: 183
Reputation: 126412
Paragraph 20.8.9.1.3 of the C++11 Standard defines the effects of calling std::bind()
in terms of the INVOKE()
pseudo-function this way:
Returns: A forwarding call wrapper
g
with a weak result type (20.8.2). The effect ofg(u1, u2, ..., uM)
shall beINVOKE (fd, std::forward<V1>(v1), std::forward<V2>(v2), ..., std::forward<VN>(vN), result_of<FD cv & (V1, V2, ..., VN)>::type)
Notice that the call to std::forward<>
will always return a reference type, be it an lvalue reference or an rvalue reference. Per Paragraphs 20.2.3/1-2, in fact:
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;
Returns:
static_cast<T&&>(t)
Therefore, the INVOKE
pseudo-function (watch out: not the bind()
function!) will be invoked with references here, and this is supported by the definition you quoted.
I believe the Standard itself never makes use of the INVOKE()
pseudo-function (which is just some internal formalism) with objects (not references to object) of a type derived from T
.
However, this doesn't meant that you cannot invoke std::bind()
or other functions whose definition is in turn given in terms of INVOKE()
with an object (not a reference to an object) of a type derived from T
.
Upvotes: 3