std :: function是否允许在其返回类型中从引用隐式转换为复制?

Jac*_*eka 12 c++ return-by-reference c++11 std-function

在下面的代码中,编译器以静默方式将return-by-copy函数指针强制转换为const-by-const-reference std :: function.调用std :: function实例时,将返回对副本的引用,并且应用程序崩溃(大多数情况下;).

通过比较的方式,普通函数指针不允许这种隐式转换,所以我想知道我是否应该向编译器供应商抱怨(在这种情况下是gcc 4.8),还是这个行为是由标准规定的?

#include <iostream>
#include <functional>

typedef std::function<const std::string&(const std::string& x)> F;

std::string bad(const std::string& x) { return x; }
const std::string& good(const std::string& x) { return x; }

typedef const std::string& (*FP)(const std::string&);

int main(int, char**) {
    std::cout << F(&good)("hello") << std::endl;
    std::cout << F(&bad)("hello") << std::endl;

    FP a = &good;
    // FP b = &bad;  Not allowed!

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

PS这是现实世界问题的简化版本,其中bad实际上是一个lambda返回某种类型的成员:

typedef std::function<const std::string&(const X& x)> F;
F f = [](const X& x) { return x->member(); };
Run Code Online (Sandbox Code Playgroud)

我们花了一段时间才能找出这个拉姆达的返回类型进行推断std::string,不const std::string&,并认为这是导致飞机坠毁.

Jan*_*dec 6

这看起来像一种角落的情况.§2.8.11.2.1/ 7中的构造函数定义说:

要求: F应该是CopyConstructible.f对于参数类型ArgTypes,应该是Callable(20.8.11.2)并返回类型R. [...]

§2.8.11.2/ 2说:

一个可调用对象f类型的F可调用对参数类型ArgTypes和返回类型R,如果expres-锡永INVOKE (f, declval<ArgTypes>()..., R),视为未计算的操作数(第5章),是公形成(20.8.2).

最后§20.8.2/ 2说:

将INVOKE(f,t1,t2,...,tN,R)定义为隐式转换为R的INVOKE(f,t1,t2,...,tN).

显然T隐式转换为T const &,因此在没有进一步限制的情况下,应该允许构造函数.

但是,调用此函数涉及返回对临时引用的临时引用,该引用甚至在返回引用之前结束,这是未定义的行为.当某些东西是未定义的行为时,实现可能会做任何事情.遗憾的是,未定义的行为仅在调用时发生,因此它仍然不严格符合在构造时检测它.

因为调用它是对象的唯一用途,所以如果被禁止则会更好.因此,这应该被认为是规范中的缺陷.

无论如何,我建议在适当的gcc邮件列表上提出它.在这种情况下,维护者愿意稍微偏离规范,或者至少他们可以提出或帮助你向C++委员会提出问题,因为他们经常使用它.