6

在下文中,结构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;
}

1 0正如预期的那样,这使用 gcc 打印。然而,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;

相反,没有成功。非静态(模板)成员函数也是如此。那么这是一个错误吗?

4

2 回答 2

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 和休息室里的人们发现了这个“愚蠢的规则”

于 2013-09-17T23:43:59.960 回答
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 one only writes auto g()without the trailing return type specification,从而避免decltype. 但是,clang对“正常功能的返回类型扣除”(N3638)的支持仅在当前 SVN 版本中可用。

auto g()成为主流(和标准)之前,必须手动计算 ' 方法的返回类型Y,这可能会很痛苦,尤其是在有很多Ys 的情况下。

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

于 2013-09-18T09:17:06.840 回答