让 foo(衍生对象) 调用 foo(Base const&) 而不是模板函数?

2023-11-29

鉴于此代码:

template< class C >
void foo( C const& o ) { o.nosuch(); }

struct Base {};
void foo( Base const& ) {}

struct Derived: Base {};

auto main() -> int
{
    Derived d;
    foo( d );       // !Invokes template.
}

...我希望调用调用为定义的重载Base,无需定义重载或模板专门化Derived.

目标是能够申请foo适用于所有种类的物体,而不仅仅是Base (and Derived) 对象,具有适用于大多数类型对象的通用实现。

如果答案能够准确解释在这种情况下重载解析如何工作,以及它如何与重载解析一起工作,那就太好了Derived定义的过载。


在出现这个问题的代码中,foo上面是一个函数模板n_items定义如下:

template< class Type >
auto n_items( Type const& o )
    -> size_t
{ return o.size(); }

template< size_t n >
auto n_items( std::bitset<n> const& o )
    -> size_t
{ return o.count(); }       // Corresponds to std::set<int>::size()

template< class Type, size_t n >
constexpr
auto n_items( Raw_array_of_<n, Type> const& a )
    -> size_t
{ return static_size( a ); }

其目的是,这应该是一个包罗万象的类型,没有定义自己的类型n_items过载。

对于基类和派生类,基类定义自定义就足够了n_items;必须为每个派生类定义它是非常多余的。


重载解析如何工作

首先我们进行名称查找和模板类型推导。为了foo(d),这给了我们两个选择:

  1. foo(Derived const&), with [C = Derived]
  2. foo(Base const&)

这些是我们可行的候选人。

然后我们确定哪一个重载是最佳可行的候选者。首先通过查看转换序列来完成此操作。在这种情况下,(1) 是精确匹配,而 (2) 涉及具有转换排名的派生到基本转换(请参阅这张桌子)。由于排名较差,候选者 (1) 是首选,因此被认为是最佳可行候选者。

如何做自己真正想做的事

最简单的方法是简单地添加第三个重载Derived:

  1. foo(Derived const&)

这里,(1)和(3)在转换顺序方面是等效的。但不是模板的函数比模板的函数更受青睐(将其视为选择最具体的选项),因此将选择 (3)。

但你不想那样做。所以选项是: make (1) 不适用于Derived或使 (2) 为Derived也以一种首选的方式。

禁用通用模板

我们可以使用 SFINAE 简单地排除源自Base:

template <class T, class = std::enable_if_t<!std::is_convertible<T*, Base*>::value>
void foo(T const& ); // new (1)

void foo(Base const& ); // same (2)

Now, (1)不再是匹配的可行候选者,因此(2)显然是首选。

重做重载,以便首选 Base

取出小费谢奥的书,我们可以这样重构重载:

template <int I> struct choice : choice<I + 1> { };
template <> struct choice<10> { };
struct otherwise { otherwise(...) {} };

template <class T> void foo(T const& val) { foo_impl(val, choice<0>{}); }

现在我们可以更喜欢那些派生自的类型Base:

template <class T, class = std::enable_if_t<std::is_convertible<T*, Base*>::value>>
void foo_impl(T const& val, choice<0> ); // new (2)

template <class T>
void foo_impl(T const& val, otherwise ); // new (1)

这改变了重载解析的工作机制,值得深入研究不同的情况。

Calling foo(d)意味着我们正在打电话foo_impl(d, choice<0> )并为我们提供了两个可行的候选者:

  1. foo_impl(Derived const&, choice<0> ), with [T = Derived]
  2. foo_impl(Derived const&, otherwise ), with [T = Derived]

在这里,第一个参数是相同的,但对于第二个参数,第一个重载是精确匹配,而第二个参数需要通过可变参数进行转换。otherwise结果将始终是单词选择,因此首选第一个重载。正是我们想要的。

Calling foo(not_a_base)另一方面,只会给我们one可行的候选人:

  1. foo_impl(NotABase const&, otherwise ), with [T = NotABase]

另一张则因推演失败而被删除。所以这无疑是最好的可行候选者,而且我们再次得到了我们想要的东西。


对于你的具体问题,我会写一些类似的内容:

template <class T>
size_t n_items(T const& cont) { return n_items(cont, choice<0>{}); }

with:

// has count?
template <class T>
auto n_items(T const& cont, choice<0> ) -> decltype(cont.count()) {
    return cont.count();
}

// else, has size?
template <class T>
auto n_items(T const& cont, choice<1> ) -> decltype(cont.size()) {
    return cont.size();
}

// else, use static_size
template <class T>
size_t n_items(T const& cont, otherwise )
    return static_size(cont);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

让 foo(衍生对象) 调用 foo(Base const&) 而不是模板函数? 的相关文章

  • C# 静态类型不能用作参数

    public static void SendEmail String from String To String Subject String HTML String AttachmentPath null String Attachme
  • C# SmtpClient编程中如何设置带有中文的附件文件名?

    我的代码如下 ContentType ct new ContentType ct MediaType MediaTypeNames Application Octet ct Name 这是一个很长的中文文件名希望能用它在附件名中 Doc A
  • 使用 ADAL v3 使用 ClientID 对 Dynamics 365 进行身份验证

    我正在尝试对我们的在线 Dynamics CRM 进行身份验证以使用可用的 API 我能找到的唯一关于执行此操作的官方文档是 https learn microsoft com en us dynamics365 customer enga
  • 如何使用 openSSL 函数验证 PEM 证书的密钥长度

    如何验证以这种方式生成的 PEM 证书的密钥长度 openssl genrsa des3 out server key 1024 openssl req new key server key out server csr cp server
  • Boost ASIO 串行写入十六进制值

    我正在使用 ubuntu 通过串行端口与设备进行通信 所有消息都必须是十六进制值 我已经在 Windows 环境中使用白蚁测试了通信设置 并得到了我期望的响应 但在使用 Boost asio 时我无法得到任何响应 以下是我设置串口的方法 b
  • Selenium - C# - Webdriver - 无法找到元素

    在 C 中使用 selenium 我试图打开浏览器 导航到 Google 并找到文本搜索字段 我尝试下面的 IWebDriver driver new InternetExplorerDriver C driver Navigate GoT
  • Android NDK 代码中的 SIGILL

    我在市场上有一个 NDK 应用程序 并获得了有关以下内容的本机崩溃报告 SIGILL信号 我使用 Google Breakpad 生成本机崩溃报告 以下是详细信息 我的应用程序是为armeabi v7a with霓虹灯支持 它在 NVIDI
  • MVC 5 中具有 ASP.NET Identity 的 Autofac 不会验证 OWIN 管道中的安全标记

    我在 MVC 5 中设置了 AutoFac 来与 ASP NET Identity 一起使用 表面上一切似乎都工作正常 即用户可以创建帐户并登录 但后来我发现 当安全标记更改时 用户不会注销 通过在 AspNetUsers 表中进行暴力破解
  • 用于在标头更改时重新编译的简单 C 项目的示例 makefile

    有谁有完整的 makefile 可以执行以下操作 如果 HEADER 文件发生更改 则重建项目 cpp 文件在 makefile 中列出 头文件未在 makefile 中列出 头文件允许与 cpp 文件具有不同的名称 部分cpp文件没有头文
  • 为什么这个 makefile 在“make clean”上执行目标

    这是我当前的 makefile CXX g CXXFLAGS Wall O3 LDFLAGS TARGET testcpp SRCS main cpp object cpp foo cpp OBJS SRCS cpp o DEPS SRCS
  • JavaScript 错误:MVC2 视图中的条件编译已关闭

    我试图在 MVC2 视图页面中单击时调用 JavaScript 函数 a href Select a JavaScript 函数 function SelectBenefit id code alert id alert code 这里 b
  • Unity手游触摸动作不扎实

    我的代码中有一种 错误 我只是找不到它发生的原因以及如何修复它 我是统一的初学者 甚至是统一的手机游戏的初学者 我使用触摸让玩家从一侧移动到另一侧 但问题是我希望玩家在手指从一侧滑动到另一侧时能够平滑移动 但我的代码还会将玩家移动到您点击的
  • 条件类型定义

    如果我有一小段这样的代码 template
  • 让网络摄像头在 OpenCV 中工作

    我正在尝试让我的网络摄像头在 Windows 7 64 位中的 OpenCV 版本 2 2 中捕获视频 但是 我遇到了一些困难 OpenCV 附带的示例二进制文件都无法检测到我的网络摄像头 最近我发现这篇文章表明答案在于重新编译一个文件 o
  • 当Model和ViewModel一模一样的时候怎么办?

    我想知道什么是最佳实践 我被告知要始终创建 ViewModel 并且永远不要使用核心模型类将数据传递到视图 这就说得通了 让我把事情分开 但什么是Model 和ViewModel一模一样 我应该重新创建另一个类还是只是使用它 我觉得我应该重
  • .NET 和 Mono 之间的开发差异

    我正在研究 Mono 和 NET C 将来当项目开发时我们需要在 Linux 服务器上运行代码 此时我一直在研究 ASP NET MVC 和 Mono 我运行 Ubuntu 发行版 想要开发 Web 应用程序 其他一些开发人员使用 Wind
  • 使用 gcc 时在头文件中查找定义的好方法是什么?

    在使用 gcc 时 有人有推荐的方法在头文件中查找定义吗 使用 MSVC 时 我只需右键单击并选择 转到定义 这非常好 我使用过 netbeans gcc 它确实有代码帮助 包括到定义的超链接 所以这是一种选择 但是 我想知道是否有任何其他
  • 在 C# 的 WebAPI 中的 ApiController 上使用“传输编码:分块”提供数据

    我需要服务分块传输使用编码数据API控制器 因为我无权访问HttpContext or the Http请求 我有点不知道在哪里写入响应以及在哪里刷新它 设置如下 public class MyController ApiControlle
  • Streamwriter 覆盖 txt 文件中的文本

    有没有什么方法可以重新打开流写入器而不创建新的写入对象 因为此时 当调用 WriteOdd 时 streamwriter 正在覆盖在它之前调用的 WriteEven public void WriteEven StreamWriter wr
  • winform c# 中的弹出窗口

    我正在开发一个需要弹出窗口的项目 但问题是我还希望能够通过表单设计器在此弹出窗口中添加文本框等 所以基本上我有一个按钮 当您单击它时 它将打开我在表单设计器中设计的另一个窗口 我一直在谷歌搜索 但还没有找到我需要的东西 所以我希望你们能帮助

随机推荐