Jor*_*eit 6 c++ multiple-inheritance ambiguity enable-if c++11
假设我有这个可变参数的基类模板:
template <typename ... Types>
class Base
{
public:
// The member foo() can only be called when its template
// parameter is contained within the Types ... pack.
template <typename T>
typename std::enable_if<Contains<T, Types ...>::value>::type
foo() {
std::cout << "Base::foo()\n";
}
};
Run Code Online (Sandbox Code Playgroud)
foo()
只有在其template-parameter至少匹配其中一个参数Base
(Contains
此帖子底部列出的实现)时,才能调用该成员:
Base<int, char>().foo<int>(); // fine
Base<int, char>().foo<void>(); // error
Run Code Online (Sandbox Code Playgroud)
现在我使用非重叠类型集定义一个从Base继承两次的派生类:
struct Derived: public Base<int, char>,
public Base<double, void>
{};
Run Code Online (Sandbox Code Playgroud)
我希望在打电话的时候
Derived().foo<int>();
Run Code Online (Sandbox Code Playgroud)
编译器会找出要使用的基类,因为它是不包含的基类的SFINAE int
.然而,GCC 4.9和Clang 3.5都抱怨模糊的电话.
我的问题是双重的:
Derived().Base<int, char>::foo<int>();
?编辑:当我添加两个using-declarations时,GuyGreer向我展示了这个调用是消除歧义的.但是,由于我提供了用户继承的基类,因此这不是一个理想的解决方案.如果可能的话,我不希望我的用户必须将这些声明(对于大型类型列表来说可能非常冗长和重复)添加到它们的派生类中.执行Contains
:
template <typename T, typename ... Pack>
struct Contains;
template <typename T>
struct Contains<T>: public std::false_type
{};
template <typename T, typename ... Pack>
struct Contains<T, T, Pack ...>: public std::true_type
{};
template <typename T, typename U, typename ... Pack>
struct Contains<T, U, Pack ...>: public Contains<T, Pack...>
{};
Run Code Online (Sandbox Code Playgroud)
Bar*_*rry 15
这是一个更简单的例子:
template <typename T>
class Base2 {
public:
void foo(T ) { }
};
struct Derived: public Base2<int>,
public Base2<double>
{};
int main()
{
Derived().foo(0); // error
}
Run Code Online (Sandbox Code Playgroud)
原因来自合并规则[class.member.lookup]:
否则(即,C不包含f的声明或结果声明集为空),S(f,C)最初为空.如果C具有基类,则在每个直接基类子对象Bi中计算f的查找集,并将每个这样的查找集S(f,Bi)依次合并为S(f,C).
- [..]
- 否则,如果S(f,Bi)和S(f,C)的声明集不同,则合并是不明确的......
由于我们的初始声明集是空的(其中Derived
没有方法),我们必须从所有基础合并 - 但我们的基础有不同的集合,因此合并失败.但是,该规则明确仅适用于C
(Derived
)的声明集为空.所以为了避免它,我们将它变为非空:
struct Derived: public Base2<int>,
public Base2<double>
{
using Base2<int>::foo;
using Base2<double>::foo;
};
Run Code Online (Sandbox Code Playgroud)
这是有效的,因为申请的规则using
是
在声明集中,using声明被替换为派生类成员(7.3.3)未隐藏或覆盖的指定成员集,
关于成员是否不同,没有评论 - 我们实际上只是提供Derived
了两个重载foo
,绕过了成员名称查找合并规则.
现在,Derived().foo(0)
毫不含糊地打电话Base2<int>::foo(int )
.
或者using
明确地为每个基础创建一个,你可以写一个收集器来完成它们:
template <typename... Bases>
struct BaseCollector;
template <typename Base>
struct BaseCollector<Base> : Base
{
using Base::foo;
};
template <typename Base, typename... Bases>
struct BaseCollector<Base, Bases...> : Base, BaseCollector<Bases...>
{
using Base::foo;
using BaseCollector<Bases...>::foo;
};
struct Derived : BaseCollector<Base2<int>, Base2<std::string>>
{ };
int main() {
Derived().foo(0); // OK
Derived().foo(std::string("Hello")); // OK
}
Run Code Online (Sandbox Code Playgroud)
在C++ 17中,您还可以打包扩展using
声明,这意味着可以将其简化为:
template <typename... Bases>
struct BaseCollector : Bases...
{
using Bases::foo...;
};
Run Code Online (Sandbox Code Playgroud)
这不仅可以缩短编写时间,而且编译效率也更高.双赢.
归档时间: |
|
查看次数: |
1479 次 |
最近记录: |