Visual C++ 无法推导模板模板参数

2024-03-14

以下 C++17 代码片段在 GCC 和 CLang 中进行编译,但在 Visual C++ 中会出现以下错误:

<source>(14): error C2672: 'f': no matching overloaded function found
<source>(14): error C2784: 'std::ostream &f(std::ostream &,const container<int> &)': could not deduce template argument for 'const container<int> &' from 'const std::vector<int,std::allocator<int>>'
<source>(5): note: see declaration of 'f'

https://godbolt.org/z/aY769qsfK https://godbolt.org/z/aY769qsfK

#include <vector>

template< template <typename...> typename container >
void f (const container< int > &)
{ }

int main()
{
    std::vector<int> seq = {1, 2, 3};
    f<std::vector>(seq); // OK
    f(seq);              // ERROR
}

请注意,此代码类似于中的答案之一为什么编译器无法推导出模板模板参数? https://stackoverflow.com/questions/63439263/why-compiler-cannot-deduce-template-template-argument

是代码的问题吗?或者 Visual C++ 中的问题?也许 C++ 标准中存在一些歧义,在 GCC 和 Visual C++ 中的解释不同?


我在使用 Visual C++ 时也遇到过这种情况,我认为在这方面Visual C++ 编译器不符合 C++17 标准并且您的代码是正确的(但您的代码无法与std::vector使用自定义分配器!)。标准容器实际上有两个模板参数:值类型和分配器(默认为std::allocator<T>)。 C++17 之前模板模板匹配要求模板参数完全匹配C++17这很宽松,包括默认参数以及。然而出于某种原因,Visual C++ 似乎仍然期待第二个模板参数std::allocator<T>并且不假设给定的默认参数。

以下部分将更详细地讨论不同标准的模板匹配。在文章的结尾,我将建议替代方案,使您的代码在所有上述编译器上进行编译,其形式为SFINAE对于 C++17 有两个两个模板参数(这样它也可以与自定义分配器一起使用)std::span适用于 C++20 及以上版本。std::span实际上根本不需要任何模板。


模板参数std::集装箱

正如文章中指出的,您链接了已经标准库的容器,例如std::vector https://en.cppreference.com/w/cpp/container/vector, std::deque https://en.cppreference.com/w/cpp/container/deque and std::list https://en.cppreference.com/w/cpp/container/list实际上有多个模板参数。第二个参数Alloc是一个策略特征,描述内存分配并具有默认值std::allocator<T>.

template<typename T, typename Alloc = std::allocator<T>>

相反std::array https://en.cppreference.com/w/cpp/container/array实际上使用了两个模板参数T对于数据类型和std::size_t N对于容器尺寸。这意味着如果有人想编写一个涵盖所有所述容器的函数,则必须转向迭代器 https://stackoverflow.com/questions/53252321/how-to-write-a-function-that-can-take-in-an-array-or-a-vector。仅在 C++20 中,有用于连续对象序列的类模板std::span https://en.cppreference.com/w/cpp/container/span(这是一种封装了上述所有内容的超级概念),可以放松这一点。

模板模板匹配和C++标准

当编写一个模板参数本身依赖于模板参数的函数模板时,您必须编写一个所谓的模板模板函数,即以下形式的函数:

template<template<typename> class T>

请注意,严格按照标准模板,模板参数必须使用class不与typenameC++17 之前。您当然可以通过一个非常小的解决方案(例如(Godbolt https://godbolt.org/z/dTMebMnd9)

template<typename Cont>
void f (Cont const& cont) {
    using T = Cont::value_type;
    return;
}

假设容器包含静态成员变量value_type然后用于定义元素的基础数据类型。这对所有人都有效std::容器(包括std::array!)但不是很干净。

对于模板模板函数,存在特定的规则,这些规则实际上从 C++14 更改为 C++17:C++17 之前模板模板参数必须是一个模板,其参数与其替换的模板模板参数的参数完全匹配。默认参数例如第二个模板参数std::容器,上述std::allocator<T>, were 不考虑(参见“模板模板参数”部分here https://en.cppreference.com/w/cpp/language/template_parameters以及“模板模板参数”部分ISO 规范工作草案第 317 页 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4713.pdf#page=325 or 最终的 C++17 ISO 规范 https://www.iso.org/standard/68564.html):

将模板模板参数 A 与模板模板相匹配 参数P,A的每个模板参数必须匹配 精确对应P的模板参数(直到 C++17)磷 必须至少与 A 一样专业(自 C++17 起).

形式上,模板模板参数 P 至少是专门化的 作为模板模板参数 A if,将以下重写为 两个函数模板,P对应的函数模板在 至少与 A 对应的函数模板一样专业 根据函数模板的部分排序规则。给定 一个发明的类模板 X,其模板参数列表为 A (包括默认参数):

  • 两个函数模板均具有相同的模板参数,分别为 P 或 A。
  • 每个函数模板都有一个函数参数,其类型是 X 的特化,模板参数对应于 来自相应函数模板的模板参数,其中,对于 模板参数列表中的每个模板参数PP 函数模板,形成对应的模板实参AA。如果 PP声明了一个参数pack,那么AA就是pack扩展PP...; 否则,AA 是 id-表达式 PP。

如果重写产生无效类型,则 P 至少不等于 专门为 A.

因此,在 C++17 之前,人们必须编写一个模板来提及手动分配器作为默认值如下。这也适用于 Visual C++,但以下所有解决方案都将排除std::array (上帝螺栓MSVC https://godbolt.org/z/hef3Yv5K3):

template<typename T, 
         template <typename Elem,typename Alloc = std::allocator<Elem>> class Cont>
void f(Cont<T> const& cont) {
    return;
}

你也可以在 C++11 中实现同样的事情可变参数模板(这样数据类型是模板参数包的第一个模板参数,分配器是第二个模板参数T) 如下 (上帝螺栓MSVC https://godbolt.org/z/eG45x8ns4):

template<template <typename... Elem> class Cont, typename... T>
void f (Cont<T...> const& cont) {
    return;
}

现在,在 C++17 中,实际上以下几行应该编译并适用于所有std::容器与std::allocator<T>(参见第 83-88 页第 5.7 节,特别是第 85 页上的“模板模板匹配”《C++ 模板:完整指南(第二版)》范德沃德等人。 https://rads.stackoverflow.com/amzn/click/com/0321714121, 戈德螺栓海湾合作委员会 https://godbolt.org/z/jK418eers).

template<typename T, template <typename Elem> typename Cont>
void f (Cont<T> const& cont) {
    return;
}

寻求通用的std::容器模板

现在,如果您的目标是使用仅保存整数作为模板参数的通用容器,并且您必须保证它也可以在 Visual C++ 上编译,那么您有以下选项:

  • 您可以使用以下方法扩展简约的不干净版本static_assert确保您使用正确的值类型(Godbolt https://godbolt.org/z/3Ejo5zdax)。这应该适用于所有类型的分配器以及std::array但不是很干净。

      template<typename Cont>
      void f (Cont const& cont) {
          using T = Cont::value_type;
          static_assert(std::is_same<T,int>::value, "Container value type must be of type 'int'");
          return;
      }
    
  • 您可以添加std::allocator<T>作为默认模板参数,其缺点是,如果有人使用带有自定义分配器的容器,并且两者都无法使用,则您的模板将无法工作std::array (Godbolt https://godbolt.org/z/Tf4szfrMn):

      template<template <typename Elem,typename Alloc = std::allocator<Elem>> class Cont>
      void f(Cont<int> const& cont) {
          return;
      }
    
  • 与您的代码类似,您可以自己指定分配器作为第二个模板参数。同样,这不适用于其他类型的分配器(Godbolt https://godbolt.org/z/vhj57MM9E):

      template<template <typename... Elem> class Cont>
      void f(Cont<int, std::allocator<int>> const& cont) {
          return;
      }
    
  • 因此,C++20 之前最简洁的方法可能是使用SFINAE https://eli.thegreenplace.net/2014/sfinae-and-enable_if/到 SFINAE out(意味着您在模板内添加某种结构,如果不满足您的要求,该结构将生成编译文件)所有其他不使用该数据类型的实现int with type_traits (std::is_same from #include <type_traits>, Godbolt https://godbolt.org/z/fnxKqxeof)

      template<typename T, typename Alloc,  
               template <typename T,typename Alloc> class Cont,
               typename std::enable_if<std::is_same<T,int>::value>::type* = nullptr>
      void f(Cont<T,Alloc> const& cont) {
          return;
      }
    

    或者哪些不是整数类型 (std::is_integral, Godbolt https://godbolt.org/z/1bMEfoveT)因为这对于模板参数来说更加灵活Alloc:

      template<typename T, typename Alloc, 
               template <typename T,typename Alloc> class Cont,
               typename std::enable_if<std::is_integral<int>::value>::type* = nullptr>
      void f(Cont<T,Alloc> const& cont) {
          return;
      }
    

    此外,这可以是。从 C++14 开始,我们还可以使用相应的别名并编写std::enable_if_t<std::is_same_v<T,int>>代替std::enable_if<std::is_same<T,int>::value>::type这使得阅读起来不那么尴尬。

  • 终于符合最新标准了C++20您甚至应该能够使用期待已久的概念(#include <concepts>) https://en.cppreference.com/w/cpp/language/constraints使用集装箱概念 https://en.cppreference.com/w/cpp/named_req/Container(另请参阅此Stackoverflow 帖子 https://stackoverflow.com/questions/60449592/how-do-you-define-a-c-concept-for-the-standard-library-containers) 例如如下 (Wandbox https://wandbox.org/permlink/vKNrcdpcxzRTT33S)

      template<template <typename> typename Cont>
      requires Container<Cont<int>>
      void f(Cont<int> const& cont) {
          return;
      }
    
  • C++20 中也存在类似的情况std::span<T>与上述所有解决方案不同的是std::array还有(Wandbox https://wandbox.org/permlink/sdNiEgppOJHZ2Ex0)

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

Visual C++ 无法推导模板模板参数 的相关文章

  • 通过 CMIS (dotCMIS) 连接到 SP2010:异常未经授权

    我正在使用 dotCMIS 并且想要简单连接到我的 SP2010 服务器 我尝试用 C 来做到这一点 如下所示http chemistry apache org dotnet getting started with dotcmis htm
  • GLKit的GLKMatrix“列专业”如何?

    前提A 当谈论线性存储器中的 列主 矩阵时 列被一个接一个地指定 使得存储器中的前 4 个条目对应于矩阵中的第一列 另一方面 行主 矩阵被理解为依次指定行 以便内存中的前 4 个条目指定矩阵的第一行 A GLKMatrix4看起来像这样 u
  • Web 客户端和 Expect100Continue

    使用 WebClient C NET 时设置 Expect100Continue 的最佳方法是什么 我有下面的代码 我仍然在标题中看到 100 continue 愚蠢的 apache 仍然抱怨 505 错误 string url http
  • 用于检查类是否具有运算符/成员的 C++ 类型特征[重复]

    这个问题在这里已经有答案了 可能的重复 是否可以编写一个 C 模板来检查函数是否存在 https stackoverflow com questions 257288 is it possible to write a c template
  • 查找c中结构元素的偏移量

    struct a struct b int i float j x struct c int k float l y z 谁能解释一下如何找到偏移量int k这样我们就可以找到地址int i Use offsetof 找到从开始处的偏移量z
  • 为什么当实例化新的游戏对象时,它没有向它们添加标签? [复制]

    这个问题在这里已经有答案了 using System Collections using System Collections Generic using UnityEngine public class Test MonoBehaviou
  • 如何使用 ICU 解析汉字数字字符?

    我正在编写一个使用 ICU 来解析由汉字数字字符组成的 Unicode 字符串的函数 并希望返回该字符串的整数值 五 gt 5 三十一 gt 31 五千九百七十二 gt 5972 我将区域设置设置为 Locale getJapan 并使用
  • 如何从 appsettings.json 文件中的对象数组读取值

    我的 appsettings json 文件 StudentBirthdays Anne 01 11 2000 Peter 29 07 2001 Jane 15 10 2001 John Not Mentioned 我有一个单独的配置类 p
  • 如何在 C 中调用采用匿名结构的函数?

    如何在 C 中调用采用匿名结构的函数 比如这个函数 void func struct int x p printf i n p x 当提供原型的函数声明在范围内时 调用该函数的参数必须具有与原型中声明的类型兼容的类型 其中 兼容 具有标准定
  • 垃圾收集器是否在单独的进程中运行?

    垃圾收集器是否在单独的进程中启动 例如 如果我们尝试测量某段代码所花费的进程时间 并且在此期间垃圾收集器开始收集 它会在新进程上启动还是在同一进程中启动 它的工作原理如下吗 Code Process 1 gt Garbage Collect
  • 什么时候虚拟继承是一个好的设计? [复制]

    这个问题在这里已经有答案了 EDIT3 请务必在回答之前清楚地了解我要问的内容 有 EDIT2 和很多评论 有 或曾经 有很多答案清楚地表明了对问题的误解 我知道这也是我的错 对此感到抱歉 嗨 我查看了有关虚拟继承的问题 class B p
  • 使用 x509 证书签署 json 文档或字符串

    如何使用 x509 证书签署 json 文档或字符串 public static void fund string filePath C Users VIKAS Desktop Data xml Read the file XmlDocum
  • 覆盖子类中的字段或属性

    我有一个抽象基类 我想声明一个字段或属性 该字段或属性在从该父类继承的每个类中具有不同的值 我想在基类中定义它 以便我可以在基类方法中引用它 例如覆盖 ToString 来表示 此对象的类型为 property field 我有三种方法可以
  • 链接器错误:已定义

    我尝试在 Microsoft Visual Studio 2012 中编译我的 Visual C 项目 使用 MFC 但出现以下错误 error LNK2005 void cdecl operator new unsigned int 2
  • 对现有视频添加水印

    我正在寻找一种用 C 在视频上加水印的方法 就像在上面写文字一样 图片或文字标签 我该怎么做 谢谢 您可以使用 Nreco 视频转换器 代码看起来像 NReco VideoConverter FFMpegConverter wrap new
  • 如何在Xamarin中删除ViewTreeObserver?

    假设我需要获取并设置视图的高度 在 Android 中 众所周知 只有在绘制视图之后才能获取视图高度 如果您使用 Java 有很多答案 最著名的方法之一如下 取自这个答案 https stackoverflow com a 24035591
  • 混合 ExecutionContext.SuppressFlow 和任务时 AsyncLocal.Value 出现意外值

    在应用程序中 由于 AsyncLocal 的错误 意外值 我遇到了奇怪的行为 尽管我抑制了执行上下文的流程 但 AsyncLocal Value 属性有时不会在新生成的任务的执行范围内重置 下面我创建了一个最小的可重现示例来演示该问题 pr
  • 测试用例执行完成后,无论是否通过,如何将测试用例结果保存在变量中?

    我正在使用 NUNIT 在 Visual Studio 中使用 Selenium WebDriver 测试用例的代码是 我想在执行测试用例后立即在变量中记录测试用例通过或失败的情况 我怎样才能实现这一点 NUnit 假设您使用 NUnit
  • C# 模拟VolumeMute按下

    我得到以下代码来模拟音量静音按键 DllImport coredll dll SetLastError true static extern void keybd event byte bVk byte bScan int dwFlags
  • 如何防止用户控件表单在 C# 中处理键盘输入(箭头键)

    我的用户控件包含其他可以选择的控件 我想实现使用箭头键导航子控件的方法 问题是家长控制拦截箭头键并使用它来滚动其视图什么是我想避免的事情 我想自己解决控制内容的导航问题 我如何控制由箭头键引起的标准行为 提前致谢 MTH 这通常是通过重写

随机推荐

  • 如何在d3中加载geojson文件的geometryCollection

    我使用 Mapshapers 成功将英国地图 shapefile 转换为 geojson 但我无法在 d3 js 中加载此 geojson 请帮忙 脚本如下
  • 如何用onclick确认并调用函数

    我可以调用 JavaScript 函数吗return confirm 在 HTML onclick 事件中还是我需要执行包含确认和调用另一个函数的函数
  • 将现有文件夹放入存储库

    我认为直接在记事本中修改文件可能很危险 是时候进行一些适当的源代码控制了 我对源代码控制很陌生 我已在我的服务器上安装了 VisualSVN 服务器 并在我的客户端计算机上安装了 TortoiseSVN 我可以很好地创建新文件夹 检查它们等
  • 如何在Python中简单地计算时间序列的滚动/移动方差?

    我有一个简单的时间序列 我正在努力估计移动窗口内的方差 更具体地说 我无法弄清楚与实现滑动窗口函数的方式有关的一些问题 例如 当使用 NumPy 且窗口大小 20 时 def rolling window a window shape a
  • 为什么我必须将 async 关键字添加到具有 wait 关键字的函数中?

    我只想等待进程完成 不想使函数异步 请参阅下面的代码 我必须使 getUserList 异步 因为函数中有一个await 关键字 因此 我还必须编写 await UsersService getUserList 之类的内容来执行该方法 并且
  • Git/Eclipse 工作流程

    我使用 Eclipse 来编辑 Java 并使用 Git 作为我的 VCS 我应该将本地存储库放在工作区中还是工作区之外 并在每次要提交时复制文件 我知道我可以通过各种方式做到这一点 但我是 Git 新手 想知道哪种方式更好 目前最好的做法
  • 带默认子级的 Vue.js 嵌套路由

    我对 Vue js 2 中的默认子路由有疑问 当我最初访问 localhost listings 时 它会正确加载 index vue 和 map vue 作为子项 当我使用 router link 导航到 localhost listin
  • 如何在加载网页之前运行内容脚本?

    我想更改一些 dom 内容 使所有输入字段值都是 在用户可以看到网页之前先对其进行显示 我该怎么做 有什么办法可以做到这一点吗 谢谢 您需要一个运行于文档开始 https developer chrome com extensions co
  • 如何查找具有相同字段的mongo文档

    我有一个 mongo 集合 我需要在该集合中查找文档 其中字段名称和地址相等 我找了很多 只能找到MongoDb 比较 2 个字段的查询条件 https stackoverflow com questions 4442453 mongodb
  • dig (DNS Lookup) 指定 Windows 上的 DNS 服务器

    在Linux中 我会使用dig使用以下命令指定 DNS 服务器 127 0 0 1 dig google com 127 0 0 1 我安装了 Windows 的绑定工具 choco install bind toolsonly 我怎样才能
  • 带 Spring Boot 项目的 Modbus 脉冲线圈

    必须实施呼叫数字IO with Modbus协议 on 春季启动项目构建者Maven 它应该是在身体上有一定持续时间的脉冲呼叫 例如 5 秒等 以下是规范中的一个片段 关于响应和错误的更多信息 看来这个呼叫应用程序应该表现得像一个大师 在这
  • Linux:在有限空间上使用分割

    我的 Linux 机器上有一个巨大的文件 该文件约为 20GB 我的盒子上的空间约为 25GB 我想将文件分成约 100mb 的部分 我知道有一个 分割 命令 但它保留了原始文件 我没有足够的空间来保存原件 关于如何实现这一点有什么想法吗
  • 如何在 Node.js Express 中检查会话?

    我尝试检查 Express 4 中的会话是否存在 if req session user undefined 它给了我错误 Cannot read property user of undefined 如何检查会话中是否存在值 来自sour
  • Python 类中的属性是否共享? [复制]

    这个问题在这里已经有答案了 下面的代码让我很困扰 class mytest name test1 tricks list def init self name self name name self tricks name self tri
  • P_SHA1算法在PHP中的实现

    我们正在尝试实现一个函数 P SHA1 意味着 PHP 用 Python 编写的函数的模式 但不幸的是 有些东西无法正常工作 JAVA中的实现函数如下 http ws apache org wss4j xref org apache ws
  • .wav 从 AVAssetWritter ios 转换为任何压缩形式

    那么我现在面临的问题是尺寸问题 我允许用户从他们的库中选择一首歌曲 然后将其切成碎片 然后能够在启用文件共享的情况下在计算机上使用 wav 或 mp3 文件 基本上我正在使用以下 AVAssetWritter 选项 并且我不断收到一个巨大的
  • ggplot2:仅显示一组中的文本标签

    我的设置 我有一些篮球运动员和他们的统计数据 library tidyverse df lt tibble season c 2010 2011 2012 2013 2014 2010 2011 2012 2013 2014 player
  • 使用 XmlSerializer 的多个命名空间

    这是场景 我有嵌套类 需要在 xml 文档中序列化 XmlRoot Namespace http www foo bar myschema public class root XmlAttribute public string versi
  • `文件中的错误(con,“r”):无法通过运行 BRugsFit() 打开连接

    我有一个错误关于Error in file con r cannot open the connection从运行 BRugsFit 开始 我假设所有输入参数都很好 调用代码是 gt output BRugsFit model txt da
  • Visual C++ 无法推导模板模板参数

    以下 C 17 代码片段在 GCC 和 CLang 中进行编译 但在 Visual C 中会出现以下错误