在下文中,struct Y
重载了X
的成员函数f
。两个重载都是模板函数,但要明确指定不同的参数(typename
和int
):
struct X
{
template <typename> static bool f() { return true; }
};
struct Y : public X
{
using X::f;
template <int> static bool f() { return false; }
};
int main()
{
std::cout << Y::f <void>() << " " << Y::f <0>() << std::endl;
}
按预期使用gcc打印1 0
。然而,clang(3.3)抱怨
[...] error: no matching function for call to 'f'
std::cout << Y::f <void>() << " " << Y::f <0>() << std::endl;
^~~~~~~~~~~
[...] note: candidate template ignored: invalid explicitly-specified argument
for 1st template parameter
template <int> static bool f() { return false; }
^
即只能查看Y
的版本。我试过了
using X::template f;
相反,没有成功。非静态(模板)成员函数也是如此。这是一个错误吗?
答案 0 :(得分:6)
最近根据另一个答案向我解释了这个难题。
来自#clang IRC频道:
[01:16:23] <zygoloid> Xeo: this is a weird corner of the language where clang conforms but the rule is silly
[01:16:31] <Xeo> ... really? :(
[01:16:45] <zygoloid> Xeo: when deciding whether a using-declaration is hidden, we're not allowed to look at the template-parameter-list (nor the return type, iirc)
[01:17:04] <zygoloid> so the derived class declaration of operator()(T) suppresses the using-declaration
[01:17:19] <Xeo> because it has the same signature / parameter types?
[01:17:40] <zygoloid> rigth
解决方法是在f
派生版本的类中不定义uses
。相反,将它移动到一个辅助助手类(在这种情况下,它会引发问题,你认为哪个定义应该获胜)。
请参阅此处查看我之前遇到的有问题的案例:Lambda functions as base classes
以下是如何使用额外的基类修复它:
致谢感谢@Xeo和Lounge中的人员发掘这个“愚蠢的规则”
答案 1 :(得分:2)
令人非常失望的是,这种约束存在并且在C ++ 11中没有放松(可能有充分的理由,但我无法想象为什么)。我觉得它打败了阶级等级的整个概念。
无论如何,这是我找到的一个解决方法。我已经包含了另一个非静态的函数g
来说明差异,因为这个案例是我的主要兴趣。
template <typename Y>
struct X
{
template <typename> static bool f() { return true; }
template <typename> bool g() { return true; }
template <int I>
static bool f() { return Y::template _f <I>(); }
template <int I>
bool g()
{
return static_cast <Y&>(*this).template _g <I>();
}
};
class Y : public X <Y>
{
friend class X <Y>;
template <int> static bool _f() { return false; }
template <int> bool _g() { return false; }
};
int main()
{
Y y;
std::cout << Y::f <void>() << " " << Y::f <0>() << std::endl;
std::cout << y. g <void>() << " " << y. g <0>() << std::endl;
}
所以所有重载都发生在基类X
中,它通过将Y
作为模板参数来实现静态多态性(幸运的是,我的项目已经是这种情况所以我不会改变设计)。
实际Y
的实现位于私有函数_f
,_g
中。当有许多派生类(如Y
,每个派生类只有一个重载,以及单个基类X
具有多个其他重载时,此设计很好。在这种情况下,避免了大量的代码重复。同样,我的项目就是这种情况。
X
不需要知道这些函数的返回值。不幸的是,它确实需要知道返回类型:我试过例如auto g() -> decltype(...)
并且此decltype
仅适用于gcc。启用c ++ 1y只能编写auto g()
而没有尾随返回类型规范,从而避免了decltype
的问题。但是,clang's support表示“正常函数的返回类型推导”(N3638)仅在当前SVN版本中可用。
在auto g()
成为主流(和标准)之前,必须手动计算Y
方法的返回类型,这可能会很痛苦,尤其是如果有很多Y
第
对我来说,它看起来仍然像一团糟,但至少不是一个完整的。