Pregunta

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) when f is a pointer to a member function of a class T and t1 is an object of type T or a reference to an object of type T or a reference to an object of a type derived from T;

((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is not one of the types described in the previous item;

t1.*f when N == 1 and f is a pointer to member data of a class T and t1 is an object of type T or a reference to an object of type T or a reference to an object of a type derived from T;

(*t1).*f when N == 1 and f is a pointer to member data of a class T and t1 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
  • or a reference to an object of type T
  • or a reference to an object of a type derived from 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
  • or an object of a type derived from T
  • or a reference to an object of type T
  • or a reference to an object of a type derived from 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?

¿Fue útil?

Solución

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 of g(u1, u2, ..., uM) shall be INVOKE (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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top