0

下面的示例得到以下编译错误:

test.cpp: In function ‘int main(int, char**)’:
test.cpp:26:8: error: no match for call to ‘(Derived) (p1&)’
test.cpp:14:8: note: candidate is:
test.cpp:16:10: note: void Derived::operator()(const p2&)
test.cpp:16:10: note:   no known conversion for argument 1 from ‘p1’ to ‘const p2&’

据我了解,这在 C++11 中发生了变化,因此您不需要放入 using 语句。这不正确吗?有没有其他方法可以解决这个问题?

示例(使用 gcc 4.7 编译,使用 --std=c++11):

#include <iostream>
#include <string>

using namespace std;

struct p1{};
struct p2{};

struct Base
{
    void operator()(const p1&) { cout << "p1" << endl; }
};

struct Derived : public Base
{
    void operator()(const p2&) { cout << "p2" << endl; }
    //Works if I include: using Base::operator();
};

int main(int argc, char** argv)
{
    p1 p;
    p2 pp;
    Derived d;

    d(p);
    d(pp);
}
4

3 回答 3

3

据我了解,这在 C++11 中发生了变化,因此您不需要放入 using 语句。这不正确吗?

不,成员函数仍然可以隐藏在 C++11 中。

有没有其他方法可以解决这个问题?

使用声明是预期的补救措施。

于 2012-07-12T22:04:09.550 回答
3

据我所知,不,这在 C++11 中没有改变。

而它没有改变的原因是,这种行为并非偶然。该语言的设计是这样的。它有优点也有缺点,但这并不是因为标准委员会的人忘记了它而发生的事情。

不,没有办法解决它。这就是成员查找在 C++ 中的工作方式

于 2012-07-12T22:04:54.637 回答
2

只是为了澄清情况:我无法想象这在 C++ 中会发生变化甚至希望使它成为一个站得住脚的改变,您必须加强类型安全,使其不再与 C 兼容(例如,您几乎必须消除所有隐式转换)。

情况相当简单:现在,名称查找在找到的第一个范围内停止,其中包含至少一个正在使用的名称实例。如果该名称的实例与您尝试使用该名称的方式不匹配,则编译失败。

考虑一个明显的替代方案:编译器不是在该点停止搜索,而是继续搜索范围,基本上创建了所有这些名称的重载集,然后选择最匹配的那个。

在这种情况下,外部范围内看似微不足道的更改可能(完全)改变某些代码的含义,这完全是无意的。考虑这样的事情,例如:

int i;

int main() { 
    long i;

    i = 1;

    std::cout << i;
    return 0;
}

在当前的规则下,这意味着明确和明确:i=1;将值 1 分配给i定义的 local to main

根据修订后的规则,这值得商榷——事实上,情况可能不应该如此。编译器会找到 , 的两个实例i,并且由于1具有 type int,因此它可能应该与全局匹配i。当我们 print out 时i,编译器会发现一个需要 a 的重载long,因此它会打印出本地i(仍然包含垃圾)。

请注意,这增加了另一个问题: thecout << i;将指的是本地i ,因为有一个可以使用它的重载。因此,不是控制所使用的重载的变量类型,而是可用的重载也控制所使用的变量。我不确定,但我猜这会使解析变得更加困难(很可能是 NP-hard 或 NP-complete 问题)。

简而言之,任何(有意或无意)在内部范围内使用几乎任何类型的隐式转换的代码,在外部范围内看似不相关的更改可能会突然完全改变该代码的含义 - 就像上面的示例一样,很容易打破它在这个过程中相当彻底。

在上面的例子中,只有六行代码,很容易弄清楚发生了什么。但是,考虑一下,当(例如)您在头文件中定义一个类,然后将该头文件包含到其他文件中时会发生什么——编译器会查看您包含头文件的其他代码,找到更好的匹配,然后突然编码你发誓经过彻底审查和最引人注目的测试。

有了标题,它会(或至少可能)变得更糟。您定义您的类,并将标题包含到两个不同的文件中。其中一个文件在外部范围内定义了变量、函数等,而另一个则没有。现在使用名称 X 的一个文件中的代码指的是全局,而另一个文件中的代码指的是本地,因为全局在该文件中不可见。这将完全破坏模块化,并使几乎所有代码都完全破坏(或至少是可破坏的)。

当然,还有其他的可能性可能不会完全被打破。一种可能性是消除所有隐式转换,因此只会考虑完美的类型匹配。这将消除大多数明显的问题,但只是以完全破坏与 C 的兼容性为代价(更不用说可能会让很多程序员非常不高兴)。另一种可能性是像现在一样进行搜索,在找到匹配项的第一个作用域处停止——然后当且仅当在内部作用域使用该名称时编译将失败时才继续到外部作用域。

这些中的任何一个都可以工作,但是(至少)你需要相当多的限制来防止它们导致几乎疯狂的混乱程度。举个例子,考虑像a =1; a = '\2';现在这样的东西,那些必须引用同一个变量——但使用这些规则中的第一个,它们不会。

在某些特殊情况下,您可能也可以消除这种特殊情况——例如,使用当前规则来查找变量名,而新的/单独的规则仅用于函数名。

总结:简单/明显的方法会创建一种几乎无法挽回的语言。可以进行修改以防止这种情况发生,但代价是放弃与 C 和基本上所有现有 C++ 代码的兼容性。后者可能在一种全新的语言中是可能的,但是对于像 C++ 这样已经很成熟的语言(尤其是在很大程度上基于向后兼容性而建立的语言——再次是 C++)。

于 2012-07-12T23:46:59.903 回答