【发布时间】:2017-06-08 07:34:27
【问题描述】:
首先,考虑这段 C++ 代码:
#include <stdio.h>
struct foo_int {
void print(int x) {
printf("int %d\n", x);
}
};
struct foo_str {
void print(const char* x) {
printf("str %s\n", x);
}
};
struct foo : foo_int, foo_str {
//using foo_int::print;
//using foo_str::print;
};
int main() {
foo f;
f.print(123);
f.print("abc");
}
正如标准所期望的那样,这无法编译,因为print 在每个基类中被单独考虑以进行重载解析,因此调用是模棱两可的。在 Clang (4.0)、gcc (6.3) 和 MSVC (17.0) 上就是这种情况 - 请参阅 Godbolt 结果here。
现在考虑下面的sn-p,唯一的区别是我们使用operator()而不是print:
#include <stdio.h>
struct foo_int {
void operator() (int x) {
printf("int %d\n", x);
}
};
struct foo_str {
void operator() (const char* x) {
printf("str %s\n", x);
}
};
struct foo : foo_int, foo_str {
//using foo_int::operator();
//using foo_str::operator();
};
int main() {
foo f;
f(123);
f("abc");
}
我希望结果与前一个案例相同,但是it is not the case - 虽然 gcc 仍然抱怨,但 Clang 和 MSVC 可以编译这个!
问题 #1:在这种情况下谁是正确的?我希望它是 gcc,但是其他两个不相关的编译器在这里给出始终不同的结果这一事实让我想知道我是否在标准中遗漏了一些东西,并且当运算符不使用函数语法调用时,它们的情况是不同的。
还请注意,如果您只取消注释 using 声明之一,而不取消注释另一个,那么所有三个编译器都将无法编译,因为它们在重载解析期间只会考虑 using 引入的函数,并且因此,其中一个调用将由于类型不匹配而失败。记住这一点;我们稍后再讨论。
现在考虑以下代码:
#include <stdio.h>
auto print_int = [](int x) {
printf("int %d\n", x);
};
typedef decltype(print_int) foo_int;
auto print_str = [](const char* x) {
printf("str %s\n", x);
};
typedef decltype(print_str) foo_str;
struct foo : foo_int, foo_str {
//using foo_int::operator();
//using foo_str::operator();
foo(): foo_int(print_int), foo_str(print_str) {}
};
int main() {
foo f;
f(123);
f("foo");
}
再次,和以前一样,除了现在我们没有明确定义operator(),而是从 lambda 类型中获取它。同样,您希望结果与之前的 sn-p 一致;这适用于both using declarations are commented out 或both are uncommented 的情况。但是,如果您只注释掉一个而不注释另一个,那么事情就是suddenly different again:现在只有 MSVC 会像我期望的那样抱怨,而 Clang 和 gcc 都认为这很好 - 并且使用两个继承的成员来解决重载问题,尽管只有一个被using带进来!
问题 #2:在这种情况下谁是正确的?同样,我希望它是 MSVC,但是为什么 Clang 和 gcc 不同意呢?而且,更重要的是,为什么这与之前的 sn-p 不同呢?我希望 lambda 类型的行为与重载 operator() 的手动定义类型完全相同...
【问题讨论】:
标签: c++ lambda language-lawyer multiple-inheritance overload-resolution