Pav*_*aev 24 c++ lambda multiple-inheritance language-lawyer overload-resolution
首先,考虑一下这个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");
}
Run Code Online (Sandbox Code Playgroud)
正如根据标准所预期的那样,这无法编译,因为print为了重载解析的目的在每个基类中单独考虑,因此调用是不明确的.这是Clang(4.0),gcc(6.3)和MSVC(17.0)的情况 - 请参阅此处的 godbolt结果.
现在考虑以下片段,唯一的区别是我们使用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");
}
Run Code Online (Sandbox Code Playgroud)
我希望结果与之前的情况相同,但事实并非如此 - 而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");
}
Run Code Online (Sandbox Code Playgroud)
同样,和以前一样,除了现在我们没有operator()明确定义,而是从lambda类型获取它.同样,您希望结果与之前的代码段保持一致; 对于两个using声明都被注释掉,或者两个声明都被取消注释的情况,情况都是如此.但是如果你只注释掉一个而不是另一个,那么事情就会突然变得不同了:现在只有MSVC会按照我的预期抱怨,而Clang和gcc都认为这很好 - 并且使用两个继承的成员进行重载解析,尽管只有一个被带进来using!
问题2:在这种情况下谁是正确的?再一次,我希望它是MSVC,但那么为什么Clang和gcc都不同意?而且,更重要的是,为什么这与前面的代码片段不同?我希望lambda类型的行为与重载的手动定义类型完全相同operator()...
巴里排名第一.你的#2命中了一个角落:无捕获的非泛型lambdas有一个隐式转换为函数指针,它在不匹配的情况下使用.也就是说,给定
struct foo : foo_int, foo_str {
using foo_int::operator();
//using foo_str::operator();
foo(): foo_int(print_int), foo_str(print_str) {}
} f;
using fptr_str = void(*)(const char*);
Run Code Online (Sandbox Code Playgroud)
f("hello")相当于f.operator fptr_str()("hello"),转换foo为指向函数的指针并调用它.如果你编译,-O0你可以在它被优化之前实际看到对程序集中的转换函数的调用.将init-capture放入print_str,你会看到一个错误,因为隐式转换消失了.
有关更多信息,请参阅[over.call.object].
| 归档时间: |
|
| 查看次数: |
645 次 |
| 最近记录: |