【问题标题】:using and overloading a template member function of a base class?使用和重载基类的模板成员函数?
【发布时间】:2013-09-22 13:55:29
【问题描述】:

下面,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;

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

【问题讨论】:

  • X 中有趣的模板
  • 注意:我尝试了另一个版本,其中自动推导出模板参数,并且在两个编译器中都可以使用(但我需要明确的说明)。
  • @iavr:另一方面,您定义 main() 的方式不可移植。
  • @thokra 怎么样?如果程序员省略了来自main() 的return 语句,则标准要求C++ 编译器插入return 0;
  • @PetrBudnik:参数列表呢?

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


【解决方案1】:

最近根据另一个答案向我解释了这个难题。

来自#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

解决方法是uses 派生版本的类中定义f。相反,将它移到一个辅助辅助类中(在这种情况下,它引出了一个问题,你认为哪个定义应该获胜)。

致谢感谢@Xeo 和Lounge 中的人们发掘了这个“愚蠢的规则”

【讨论】:

  • 谢谢,所以我想这是个坏消息。您能否更具体地说明解决方法?你的意思是 this solution 涉及 lambda 吗?因为我需要明确指定的模板参数,但我不知道它是如何适合的。
  • @iavr 我无法分辨,因为在您的情况下,功能模板完全相互影响。您可能应该使用 SFINAE 屏蔽一部分重载,以便“另一个”继承的模板可以启动。确保使 SFINAE 分支不重叠,否则您最终会得到对 f 的模棱两可的调用或(最好的情况)
  • 据我所知,没有任何条件可以与 SFINAE 核实。两个重载同样有效,并期望由调用者指定。
  • @iavr 那么,您是否根据定义需要消除歧义?使用不同的函数名,添加一个tag 模板参数,可行吗?我现在要去看你的自我回答,之前没看过
  • @sehe 消歧之后是调用者明确指定模板参数(例如f &lt;void&gt;() vs f &lt;0&gt;())。
【解决方案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() -&gt; decltype(...)decltype 仅适用于 gcc。启用 c++1y 仅写入 auto g() 而没有尾随返回类型规范,从而避免 decltype 的问题。但是,clang's support 用于“正常功能的返回类型扣除”(N3638)仅在当前 SVN 版本中可用。

auto g() 成为主流(和标准)之前,必须手动计算Y 的方法的返回类型,这可能会很痛苦,特别是如果有很多Ys。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-10-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-02
    • 1970-01-01
    • 1970-01-01
    • 2022-01-16
    相关资源
    最近更新 更多