使用和重载基类的模板成员函数?

时间:2013-09-17 23:25:54

标签: c++ overloading template-function using-declaration member-functions

在下文中,struct Y重载了X的成员函数f。两个重载都是模板函数,但要明确指定不同的参数(typenameint):

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;
相反,没有成功。非静态(模板)成员函数也是如此。这是一个错误吗?

2 个答案:

答案 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。相反,将它移动到一个辅助助手类(在这种情况下,它会引发问题,你认为哪个定义应该获胜)。

致谢感谢@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

对我来说,它看起来仍然像一团糟,但至少不是一个完整的。