【发布时间】:2019-10-20 11:50:50
【问题描述】:
以下代码在 clang 上产生不同的结果。
#include <iostream>
struct Dummy1 {};
struct Dummy2 {};
struct A {
virtual void foo(Dummy1) {
std::cout << "A" << std::endl;
}
virtual void foo(Dummy2) {
std::cout << "A" << std::endl;
}
};
template<class T>
struct C : virtual A {
using A::foo;
void foo(Dummy2) override {
std::cout << "C" << std::endl;
}
};
template<class T>
struct B : virtual A {
using A::foo;
void foo(Dummy1) final {
std::cout << "B" << std::endl;
}
};
template<class T>
struct D : B<T>, C<T> {
// using B<T>::foo; // error: call to member function 'foo' is ambiguous
// using C<T>::foo; // error: call to member function 'foo' is ambiguous
using A::foo;
};
int main() {
D<int> d;
d.foo(Dummy1{});
d.foo(Dummy2{});
A& a = d;
a.foo(Dummy1{});
a.foo(Dummy2{});
B<int>& b = d;
b.foo(Dummy1{});
b.foo(Dummy2{});
C<int>& c =d;
c.foo(Dummy1{});
c.foo(Dummy2{});
return 0;
}
gcc(版本 4.8.1 - 9.1)、icc(版本 16、17、19)、Visual Studio 2017 15.4.0 Preview 1.0、Visual Studio 2013 12.0.31101.00 Update 4、clang(版本 3.4.1 - 3.9。 1)
全部给出以下输出,这是我所期望的:
B
C
B
C
B
C
B
C
只选择方法C<T>::foo(Dummy1)和B<T>::foo(Dummy2),不使用方法A<T>::foo。
Clang(版本 4.0.0 - 8.0.0)在通过 D<T> 对象调用时选择 A::foo(Dummy2),并且仅在那时。通过引用 B<T> 调用时 - 选择 C<T>::foo(Dummy2)。
B
A <-- difference
B
C
B
C
B
C
当派生类的顺序变为struct D : C<T>, B<T>时,输出变为:
A <--
C
B
C
B
C
B
C
似乎对于第二个派生类方法foo 不被认为是虚拟的。
只有 Visual Studio 会发出警告,而 C4250 没有那么有用。
将using B<T>::foo; 和using C<T>::foo; 写入D<T> 而不是using A::foo; 会使clang 产生以下错误:
错误:对成员函数 'foo' 的调用不明确
在 gcc 上的行为没有改变,代码编译和输出是一样的。
这里的正确行为是什么?
由于应用程序给出不同的结果,有没有办法找到这种构造的所有相似实例或采取一些解决方法?我必须同时使用 gcc 和 clang 进行编译。检查是否存在比我发现的更多地方的相同问题可能很困难。
【问题讨论】:
-
对于显示的程序,clang 绝对是错误的。对于在
D中有两个using声明的版本,[namespace.udecl]/15 远不清楚会发生什么。前一个 using-declarator 的成员是否“在派生类中”? -
我也认为是clang bug;通过不合格的函数调用选择
A::foo不应禁用虚拟调度 -
这是另一个不使用函数重载的测试用例wandbox.org/permlink/NGGfh7OOnRveO2zN 函数覆盖似乎被忽略,除非它们位于第一个继承的节点中,但如果你没有
using或者您正在使用对基类的引用。 -
我将错误报告添加到 clang 的问题跟踪器 bugs.llvm.org/show_bug.cgi?id=42211。
标签: c++ gcc clang overloading language-lawyer