- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
在尝试依赖可变参数模板实现一些事情时,我偶然发现了一些我无法解释的事情。我将问题归结为以下代码片段:
template <typename ... Args>
struct A {};
template <template <typename...> class Z, typename T>
struct test;
template <template <typename...> class Z, typename T>
struct test<Z, Z<T>> {
static void foo() {
std::cout << "I'm more specialized than the variadic spec, hehe!" << std::endl;
}
};
template <template <typename...> class Z, typename T, typename ... Args>
struct test<Z, Z<T, Args...>> {
static void foo() {
std::cout << "I'm variadic!" << std::endl;
}
};
int main() {
test<A, A<int>>::foo();
}
在 gcc 下,它会产生错误,因为它在尝试实例化 test<A, A<int>>
时认为两种特化是同等特化的。 :
main.cpp: In function 'int main()':
main.cpp:25:24: error: ambiguous template instantiation for 'struct test<A, A<int> >'
test<A, A<int>>::foo();
^~
main.cpp:11:12: note: candidates are: template<template<class ...> class Z, class T> struct test<Z, Z<T> > [with Z = A; T = int]
struct test<Z, Z<T>> {
^~~~~~~~~~~~~
main.cpp:18:12: note: template<template<class ...> class Z, class T, class ... Args> struct test<Z, Z<T, Args ...> > [with Z = A; T = int; Args = {}]
struct test<Z, Z<T, Args...>> {
但是,clang 认为第一个特化“更特化”(通过部分排序:请参阅下一节),因为它编译良好并打印:
I'm more specialized than the variadic spec, hehe!
一个 live demo 可以在 Coliru 上找到。我也尝试使用 gcc 的 HEAD 版本,得到了同样的错误。
我的问题是:由于这两个著名的编译器的行为不同,哪一个是正确的,这段代码是正确的 C++ 吗?
根据 C++14 标准草案的 §14.5.5.1 和 $14.5.5.2 部分,会触发部分排序以确定应选择哪个特化:
(1.2) — If more than one matching specialization is found, the partial order rules (14.5.5.2) are used to determine whether one of the specializations is more specialized than the others. If none of the specializations is more specialized than all of the other matching specializations, then the use of the class template is ambiguous and the program is ill-formed.
现在根据 §14.5.5.2,类模板特化通过这个过程转换为函数模板:
For two class template partial specializations, the first is more specialized than the second if, given the following rewrite to two function templates, the first function template is more specialized than the second according to the ordering rules for function templates (14.5.6.2):
(1.1) — the first function template has the same template parameters as the first partial specialization and has a single function parameter whose type is a class template specialization with the template arguments of the first partial specialization, and
(1.2) — the second function template has the same template parameters as the second partial specialization and has a single function parameter whose type is a class template specialization with the template arguments of the second partial specialization.
因此,我尝试使用上述转换应生成的函数模板重载来重现问题:
template <typename T>
void foo(T const&) {
std::cout << "Generic template\n";
}
template <template <typename ...> class Z, typename T>
void foo(Z<T> const&) {
std::cout << "Z<T>: most specialized overload for foo\n";
}
template <template <typename ...> class Z, typename T, typename ... Args>
void foo(Z<T, Args...> const&) {
std::cout << "Z<T, Args...>: variadic overload\n";
}
现在尝试像这样使用它:
template <typename ... Args>
struct A {};
int main() {
A<int> a;
foo(a);
}
在 clang 和 gcc 中都会产生编译错误 [模糊调用]: live demo 。我预计 clang 至少会具有与类模板案例一致的行为。
然后,这是我对标准的解释(我似乎与@Danh 分享),所以此时我们需要一个 language-lawyer确认这一点。
注意:我浏览了一点 LLVM 的错误跟踪器,但找不到在这个问题中观察到的函数模板重载行为的票证。
最佳答案
来自 temp.class.order :
For two class template partial specializations, the first is more specialized than the second if, given the following rewrite to two function templates, the first function template is more specialized than the second according to the ordering rules for function templates (
[temp.func.order]
):
Each of the two function templates has the same template parameters as the corresponding partial specialization.
Each function template has a single function parameter whose type is a class template specialization where the template arguments are the corresponding template parameters from the function template for each template argument in the template-argument-list of the simple-template-id of the partial specialization.
顺序:
template <template <typename...> class Z, typename T>
struct test<Z, Z<T>> {
static void foo() {
std::cout << "I'm more specialized than the variadic spec, hehe!" << std::endl;
}
};
template <template <typename...> class Z, typename T, typename ... Args>
struct test<Z, Z<T, Args...>> {
static void foo() {
std::cout << "I'm variadic!" << std::endl;
}
};
取决于以下顺序:
template <template <typename...> class Z, typename T>
void bar(test<Z, Z<T>>); // #1
template <template <typename...> class Z, typename T, typename ... Args>
void bar(test<Z, Z<T, Args...>>); // #2
来自 [temp.func.order]
:
Partial ordering selects which of two function templates is more specialized than the other by transforming each template in turn (see next paragraph) and performing template argument deduction using the function type. The deduction process determines whether one of the templates is more specialized than the other. If so, the more specialized template is the one chosen by the partial ordering process.
To produce the transformed template, for each type, non-type, or template template parameter (including template parameter packs ([temp.variadic]) thereof) synthesize a unique type, value, or class template respectively and substitute it for each occurrence of that parameter in the function type of the template.
Using the transformed function template's function type, perform type deduction against the other template as described in
[temp.deduct.partial]
.
通过这些段落,对于从任何合成模板 Z0
和类型 T0
转换而来的任何函数,它可以形成 #1
,我们可以使用 #2
进行类型推导。但是从 #2
转换而来的函数,带有任何类型 T2
和任何非空 Args2
的虚构模板 Z2
不能从 #1
推导出来。 #1
显然比 #2
更专业。
clang++ 在这种情况下是正确的。
其实,this one和 this one在 g++ 和 clang 中都无法编译(因为模棱两可)。似乎两个编译器都很难使用模板模板参数。 (后一个是明确排序的,因为它的顺序与没有函数调用的顺序相同)。
关于c++ - 类模板特化优先级/歧义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40892048/
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 4 年前。
正如您在 this travis.yml 中看到的那样文件,我的代码依赖于一些第三方库,我在构建项目之前将它们安装在远程系统上。 Travis 每次推送提交时都会下载并构建这些库,这可以避免吗?我的意
我是一名优秀的程序员,十分优秀!