为什么使用声明不能解决钻石问题?
Why doesn't a using-declaration work to solve the diamond problem?
请考虑以下代码:
struct A
{
void f()
{
}
};
struct B1 : A
{
};
struct B2 : A
{
};
struct C : B1, B2
{
void f() // works
{
B1::f();
}
//using B1::f; // does not work
//using B1::A::f; // does not work as well
};
int main()
{
C c;
c.f();
return 0;
}
请不要复制粘贴关于如何解决钻石问题的标准回复("use virtual inheritance")。我在这里要问的是,为什么 using 声明在这种情况下不起作用。确切的编译器错误是:
In function 'int main()':
prog.cpp:31:6: error: 'A' is an ambiguous base of 'C'
c.f();
我的印象是 using 声明应该从这个例子中起作用:
struct A
{
void f()
{
}
};
struct B
{
void f()
{
}
};
struct C : A, B
{
using A::f;
};
int main()
{
C c;
c.f(); // will call A::f
return 0;
}
[namespace.udecl]/p17 中有一条注释直接解决了这种情况:
[ Note: Because a using-declaration designates a base class member
(and not a member subobject or a member function of a base class
subobject), a using-declaration cannot be used to resolve inherited
member ambiguities. For example,
struct A { int x(); };
struct B : A { };
struct C : A {
using A::x;
int x(int);
};
struct D : B, C {
using C::x;
int x(double);
};
int f(D* d) {
return d->x(); // ambiguous: B::x or C::x
}
—end note ]
其他人可以找到标准报价,但我将从概念上进行解释。
它不起作用,因为 using-declaration 仅影响名称查找。
您的 using-declaration 导致名称查找成功,否则会失败,也就是说,它告诉编译器在哪里可以找到函数 f
. 但它并没有告诉它 哪个 A
子对象 f
作用于 ,即哪个将作为隐式传递调用 f
时的 this
参数。
尽管 C
有两个 A
子对象,但只有一个函数 A::f
,并且它采用类型 [=19] 的隐式 this
参数=].为了在 C
对象上调用它,C*
必须隐式转换为 A*
。这总是不明确的,并且不受任何 using-declarations.
的影响
(如果将数据成员放在 A
中,则更有意义。然后 C
将有两个这样的数据成员。调用 f
时,如果它访问数据成员,它是访问继承自 B1
的 A
子对象中的成员,还是访问继承自 B2
的 A
子对象中的成员?)
除了 T.C. 的回答之外,我想补充一点,派生 class 中的名称查找在标准中的第 10.2 节中有非常详细的解释。
这里是关于处理 using-declarations 的说法:
10.2/3: The lookup set (...) consists of two component sets: the declaration set, a set of members named f; and the subobject set, a set of subobjects where declarations of these members (possibly including
using-declarations) were found. In the declaration set, using-declarations are replaced by the members they designate, and type declarations (including
injected-class-names) are replaced by the types they designate.
因此,当您尝试在 struct C
中声明时
using B1::f; // you hope to make clear that B1::f is to be used
根据查找规则,您的编译器仍然会找到可能的候选者:B1::f
和 B2::f
,因此它仍然不明确。
请考虑以下代码:
struct A
{
void f()
{
}
};
struct B1 : A
{
};
struct B2 : A
{
};
struct C : B1, B2
{
void f() // works
{
B1::f();
}
//using B1::f; // does not work
//using B1::A::f; // does not work as well
};
int main()
{
C c;
c.f();
return 0;
}
请不要复制粘贴关于如何解决钻石问题的标准回复("use virtual inheritance")。我在这里要问的是,为什么 using 声明在这种情况下不起作用。确切的编译器错误是:
In function 'int main()':
prog.cpp:31:6: error: 'A' is an ambiguous base of 'C'
c.f();
我的印象是 using 声明应该从这个例子中起作用:
struct A
{
void f()
{
}
};
struct B
{
void f()
{
}
};
struct C : A, B
{
using A::f;
};
int main()
{
C c;
c.f(); // will call A::f
return 0;
}
[namespace.udecl]/p17 中有一条注释直接解决了这种情况:
[ Note: Because a using-declaration designates a base class member (and not a member subobject or a member function of a base class subobject), a using-declaration cannot be used to resolve inherited member ambiguities. For example,
struct A { int x(); }; struct B : A { }; struct C : A { using A::x; int x(int); }; struct D : B, C { using C::x; int x(double); }; int f(D* d) { return d->x(); // ambiguous: B::x or C::x }
—end note ]
其他人可以找到标准报价,但我将从概念上进行解释。
它不起作用,因为 using-declaration 仅影响名称查找。
您的 using-declaration 导致名称查找成功,否则会失败,也就是说,它告诉编译器在哪里可以找到函数 f
. 但它并没有告诉它 哪个 A
子对象 f
作用于 ,即哪个将作为隐式传递调用 f
时的 this
参数。
尽管 C
有两个 A
子对象,但只有一个函数 A::f
,并且它采用类型 [=19] 的隐式 this
参数=].为了在 C
对象上调用它,C*
必须隐式转换为 A*
。这总是不明确的,并且不受任何 using-declarations.
(如果将数据成员放在 A
中,则更有意义。然后 C
将有两个这样的数据成员。调用 f
时,如果它访问数据成员,它是访问继承自 B1
的 A
子对象中的成员,还是访问继承自 B2
的 A
子对象中的成员?)
除了 T.C. 的回答之外,我想补充一点,派生 class 中的名称查找在标准中的第 10.2 节中有非常详细的解释。
这里是关于处理 using-declarations 的说法:
10.2/3: The lookup set (...) consists of two component sets: the declaration set, a set of members named f; and the subobject set, a set of subobjects where declarations of these members (possibly including using-declarations) were found. In the declaration set, using-declarations are replaced by the members they designate, and type declarations (including injected-class-names) are replaced by the types they designate.
因此,当您尝试在 struct C
using B1::f; // you hope to make clear that B1::f is to be used
根据查找规则,您的编译器仍然会找到可能的候选者:B1::f
和 B2::f
,因此它仍然不明确。