当前位置:网站首页>带默认模板实参的类模板与模板模板形参的匹配
带默认模板实参的类模板与模板模板形参的匹配
2022-04-23 05:47:00 【mightbxg】
首先解释一下这个看起来很奇怪的标题。
“带默认模板参数的类模板”是指 Class template with default template-parameter,即一个类模板的部分或全部模板参数有默认值,比如:
template<typename T1, typename T2 = int>
struct Foo {
};
有个很典型的例子是 std::vector
,它其实有两个模板参数:
template<class T, class Allocator = std::allocator<T>>
class vector;
只不过绝大多数情况下,我们并不需要指定第二个模板参数,感觉上好像它就只有一个模板参数,比如:std::vector<int> vec;
。
“模板模板参数”即 Template template parameter,也就是带模板的模板参数,比如:
template <template <typename> class C, typename T>
void bar(const C<T>& c) {
}
这里面的 C
就是一个模板模板参数。
现在,问题来了,在已有上述类 Foo
和函数 bar()
定义的情况下,下面这个代码能够通过编译吗?
bar(Foo<int>{
});
这个问题其实是在问,template<typename T1, typename T2 = int>
能不能与 template <typename> class C
进行匹配。
一方面,C
这个类模板只能有一个模板参数,而 Foo
是有两个模板参数的,所以看起来两者应该无法匹配。但另一方面,Foo
第二个模板参数有默认值,它在实际使用中完全可以看作只有一个模板参数,所以似乎可以匹配 C
。
该问题答案是:取决于编译器!
参见 cppreference,C++17 引入了一个新的特性 Matching template template parameters to compatible arguments (模板模板形参与可兼容实参匹配,貌似没有更简单的名字,暂且叫它 MTTP2CA 吧) 来解决 CWG 150 问题,使得以下代码能够编译:
template<class T> class A {
/* ... */ };
template<class T, class U = T> class B {
/* ... */ };
template <class ...Types> class C {
/* ... */ };
template<template<class> class P> class X {
/* ... */ };
X<A> xa; // OK
X<B> xb; // OK in C++17 after CWG 150
// Error earlier: not an exact match
X<C> xc; // OK in C++17 after CWG 150
// Error earlier: not an exact match
所以,不支持 C++17 的编译器是不能编译 bar(Foo<int>{});
的。但问题并不仅限于此,即使是目前最新的 Clang 12 和 MSVC v19,也都不行,只有 GCC 能够编译这样的代码,这是为什么呢?
原因是 MTTP2CA 特性其实有一个缺陷,假设上述 bar()
函数新增一个定义:
template <template <typename, typename> class C, typename T1, typename T2>
void bar(const C<T1, T2>& c) {
}
此时 bar(Foo<int>{});
匹配的是 void bar(const C<T>& c);
还是 void bar(const C<T1, T2>& c);
呢?
类似的,如果加入一个新的类模板:
template <class>
struct Bar;
template <template <class> class X, class T>
struct Bar<X<T>> {
};
template <template <class, class> class X, class T, class U>
struct Bar<X<T, U>> {
};
那么 Bar<Foo<int>> a;
匹配的是 Bar<X<T>>
还是 Bar<X<T, U>>
呢?
事实上这两种情况在 MTTP2CA 中是没有定义的,也就是说 C++ 标准并没有规定此时应该匹配前者(一个模板形参)还是后者(两个模板形参)。我的测试结果是 GCC 会选择匹配后者。
因为这个未定义的情况,Clang 和 MSVC 在默认情况下隐藏了这一特性。Clang 可以通过 -frelaxed-template-template-args
编译选项来打开该特性,MSVC 我还没找到开启方法。
GCC 和 Clang 在该问题上的不一致还带来了一个比较神奇的现象:如果你 IDE 用的是 QtCreator,并开启了 ClangCodeModel 插件(自动分析代码问题,高版本 QtCreator 默认开启),那么在编译前面提到的代码时,QtCreator 会报错(ClangCodeModel 基于 Clang 对代码进行分析),但同时又能够编译成功(Linux 上的 QtCreator 默认使用 GCC 作为编译器)。
版权声明
本文为[mightbxg]所创,转载请带上原文链接,感谢
https://blog.csdn.net/mightbxg/article/details/119118660
边栏推荐
猜你喜欢
[leetcode 19] delete the penultimate node of the linked list
如何安装jsonpath包
Robocode教程8——AdvancedRobot
Basic knowledge of network in cloud computing
Graduation project, curriculum link, student achievement evaluation system
ArcGIS license错误-15解决方法
【UDS统一诊断服务】四、诊断典型服务(5)— 功能/元件测试功能单元(例行程序功能单元0x31)
Swagger2 generates API documents
批量导出Arcgis属性表
Robocode教程4——Robocode的游戏物理
随机推荐
Rust 中的 RefCell
Rust 中的 Cell 共享可变指针
SQL -- data filtering and grouping
定位器
Rust:在线程池中共享变量
【UDS统一诊断服务】一、诊断概述(4)— 基本概念和术语
word排版遇到的格式问题
Record the installation and configuration of gestermer on TX2, and then use GST RTSP server
SQL sorts according to the specified content
Storing inherited knowledge in cloud computing
【UDS统一诊断服务】四、诊断典型服务(4)— 在线编程功能单元(0x34-0x38)
爬西瓜视频url
Object转Json差异之Gson fastJson jackson 修改字段名
【学习一下】HF-Net 训练
Graduation project, viewing screenshots of epidemic psychological counseling system
【UDS统一诊断服务】四、诊断典型服务(2)— 数据传输功能单元
selenium+PhantomJS破解滑动验证2
jenkspy包安装
A solution to replace not in in SQL
Cf6d lizards and fundamentals 2 problem solving