限制函数指针的转换

7
我不明白为什么从类型为int (*)(const int&)的函数指针到类型为int (*)(int&)的指针进行static_cast是不合法的。如果我有一个函数指针,在技术上允许接受int&,但想要分配给它一个自愿放弃修改所指值权限的函数,难道它不能像原则上可以的函数一样表现出特例吗?
换句话说,既然我可以将任意int&传递给int (*)(const int&)函数,那么在标准中是否存在更深层次的原因,导致后者不能被视为int (*)(int&)的一个特例并分配给这样的变量? M(!W)E:
int g(const int& q) {
  return q;
}

int main() {
  int (*f)(int&) = static_cast<int(*)(int&)>(g); // breaks
  int x = 1;
  return f(x);
}

3
它们是不同类型的,标准明确规定了这种转换方式。不清楚的是什么?您想知道委员会的原因吗? - skypjack
编译器不需要传递引用给常量版本。它可能只是被实现为 const int。 - Richard Critten
好的,那将是最好的。我知道它们是不同的类型(因此需要显式转换),我的问题是为什么它们不兼容。 - The Vee
2
@TheVee 这是 static_cast 的工作原理。它们是完全不同的类型,所以它们不会很快一起工作。如果你使用了 reinterpret_cast,那么它会编译,因为它的工作方式不同,但这是未定义的行为,我不建议使用它。 - skypjack
2
转换规则不够复杂,也没有协变参数。欢迎提出更改建议。 - Kerrek SB
我也不喜欢这种行为。它不仅在const/non-const上出现问题,而且在协变类型上也有问题。标准应该允许这种情况。目前,为了避免额外的包装函数调用,我在这种情况下使用reinterpret_cast(我知道,这是未定义行为,但实际上在我使用的所有编译器中都能正常运行)。 - geza
1个回答

5

标准中是否有更深层次的原因,解释为什么这些问题通常无法回答。也许没有人认为这是值得探究的事情。总的来说,C++对协变和逆变的支持有限。有很多其他安全转换可能是可能的,不仅限于资格引用。

例如,像Animal*(*)(Dog*)这样的函数指针可以从Animal*(*)(Animal*)Dog*(*)(Dog*)两种方式进行初始化。但是今天,两种转换都不受支持。

所以真正的答案可能是:撰写提案


然而,在这种情况下,我们仍然可以通过一个明显被低估的规则来实现所需的行为:几乎总是使用Lambda表达式。我们知道我们想要使用的函数的名称,并且没有捕获(我们不需要)的Lambda表达式可以转换为函数指针:
int (*f)(int&) = [](int& i) { return g(i); };

你可以将 T * 转换为 T const* 并且 你也可以 __将 T * 视为 T const*__,因此你可以将 T * * 转换为 T const* const* 等等。你可以将 Der * 转换为 Base *,但是 **你不能将 Der * 视为 Base ***,因此你无法将 Der * * 转换为 Base *const* - curiousguy
@curiousguy 什么? - Barry
你可以通过类型为 T const * 的左值读取类型为 T* 的对象,但你不能通过继承关系相关的类型为 U* 的左值进行读取。 - curiousguy
@a curiousguy a) 你可以这样做:Base* b = new Derived b) 我仍然不明白你的评论与问题或答案有什么关系。 - Barry
你可以使用 float f = 1,但是 int 不是 float!你不能使用 int i; float &f = i;,而 int i; float &f = (float&)i; 虽然形式上正确,但行为未定义。另一方面,int const &rci = i; 是可以的,并且不会创建当前值 i 的副本(它根本不访问 ii 可能未初始化)。const float &rcf = i; 是可以的,但会复制当前值 i 的值(i 的值必须被定义)。 - curiousguy
“你可以将指针值进行转换。将其分解为基本步骤:Derived *pd = new Derived; Base *pb = pd; 第二个声明读取了 d 的值。与 Derived *&rpd = pd 形成对比,它绑定了一个引用而不读取该值。现在考虑:Base *&prb = pd; 这是不合法的。你不能进行这样的引用绑定。当你执行 Base (*const (&prb)) = pd; 时(括号不是必需的,但为了清晰起见添加了),会创建 pd 的副本(需要定义并读取该值)。” - curiousguy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接