4

这个问题基本上是我给出的这个答案的后果。我刚刚意识到标准中的措辞似乎省略了一些情况。考虑这段代码:

#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按如下方式定义INVOKE :(f, t1, t2, ..., tN)

(t1.*f)(t2, ..., tN)whenf是指向类的成员函数的指针,T并且t1是类型对象T或对类型对象的T引用或对派生于的类型对象的引用T

((*t1).*f)(t2, ..., tN)whenf是指向类的成员函数的指针,T并且t1不是上一项中描述的类型之一;

t1.*fN == 1andf是指向类的成员数据的指针T并且t1是类型对象T或对类型对象的T引用或对派生于的类型对象的引用时T

(*t1).*fwhenN == 1f是指向类的成员数据的指针,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)(...)它,对吗?这也是我应该关注的事情,还是标准在给定的上下文中清楚地定义了“源自”?

4

1 回答 1

3

C++11 标准的第 20.8.9.1.3 段定义了以这种方式调用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<>将始终返回一个引用类型,无论是左值引用还是右值引用。根据第 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.

于 2013-03-09T13:22:17.907 回答