Wat*_*ter 4 c++ templates variadic-templates template-argument-deduction c++17
我想要一个辅助函数来为我实例化一个类。目前它不能在 clang 中编译(尽管它可以在 gcc 中编译工作),但我也需要它在 clang 中工作。目前我正在使用clang version 6.0.0-1ubuntu2
.
我不确定它为什么会失败,因为 gcc 能够检测到类型。我尝试从这篇文章中做一些事情并玩了一段时间,但我一直在撞墙。MCVE 可用,或者您可以在此处在coliru上试用:
#include <vector>
using namespace std;
template <typename T, template <typename> typename Container>
struct SomeClass {
SomeClass(const Container<T>& c) {
}
};
template <typename T, template <typename> typename C>
inline auto make_some_class(const C<T>& container) {
return SomeClass<T, C>(container);
}
int main() {
vector<int> ints;
auto stuff = make_some_class(ints);
}
Run Code Online (Sandbox Code Playgroud)
main.cpp:19:18: 错误:没有匹配的函数来调用“make_some_class”
Run Code Online (Sandbox Code Playgroud)auto stuff = make_some_class(ints); ^~~~~~~~~~~~~~~
main.cpp:12:13: 注意: 候选模板被忽略: 替换失败 [with T = int]: 模板模板参数的模板参数与其对应的模板模板参数不同
Run Code Online (Sandbox Code Playgroud)inline auto make_some_class(const C<T>& container) { ^
产生了 1 个错误。
建议:尝试
#include <vector>
template <template <typename...> typename Container, typename ... Ts>
struct SomeClass {
SomeClass(const Container<Ts...>& c) {
}
};
template <template <typename...> typename C, typename ... Ts>
inline auto make_some_class(const C<Ts...>& container) {
return SomeClass<C, Ts...>(container);
}
int main() {
std::vector<int> ints;
auto stuff = make_some_class(ints);
}
Run Code Online (Sandbox Code Playgroud)
我的意思是......我想问题是std::vector
不是一个接收一个类型模板参数的容器;它是一个接收两个类型模板参数的容器(第二个具有默认类型:第一个是std::allocator<T>
哪里T
)。
所以建议是:使SomeClass
容器更灵活,并能够接收带有可变参数模板类型列表的容器
template <typename...> typename Container
Run Code Online (Sandbox Code Playgroud)
以及相应的模板类型列表
typename ... Ts
Run Code Online (Sandbox Code Playgroud)
如果你想要一个可变参数列表 ( Ts...
) 你需要它在最后一个位置所以你必须切换Container
和T
(now Ts...
)的位置:在Container
可变参数列表之前和之后Ts...
template <template <typename...> typename Container, typename ... Ts>
struct SomeClass {
SomeClass(const Container<Ts...>& c) {
}
};
Run Code Online (Sandbox Code Playgroud)
不严格要求,但为了统一,我建议以make_some_class()
相同的方式重写(显然,在模板参数列表中传递C
之前Ts...
)。
template <template <typename...> typename C, typename ... Ts>
inline auto make_some_class(const C<Ts...>& container) {
return SomeClass<C, Ts...>(container);
}
Run Code Online (Sandbox Code Playgroud)
让我们将其简化为:
template <template <typename> class C, typename T>
void foo(C<T> const&) { }
std::vector<int> v;
foo(v);
Run Code Online (Sandbox Code Playgroud)
您会发现 gcc 会编译它,但 clang 不会。两者的原因都很有趣。
首先,回想一下这std::vector
是一个带有两个模板参数的类模板:
template <typename T, typename Alloc = std::allocator<T>>
class vector { ... };
Run Code Online (Sandbox Code Playgroud)
为什么 gcc 认为匹配template <typename> class C
- 只有一个模板参数的模板?因为规则因P0522R0而改变。这是有道理的 - 我们可以vector
像在普通代码中使用一个类型参数一样使用它,因此它应该能够匹配这样的模板参数。
现在,为什么 clang 认为这vector
不匹配?因为他们明确选择不采用此规则。从他们的文档:
(10):尽管是缺陷报告的解决方案,但此功能在所有语言版本中默认处于禁用状态,并且可以
-frelaxed-template-template-args
在 Clang 4 以后的标志中显式启用。对标准的更改缺少模板偏序的相应更改,从而导致对合理且先前有效的代码产生歧义错误。预计此问题将很快得到纠正。
也就是说,它可能会破坏代码。
当然,您可能只想知道如何修复它。只需更改模板模板参数的声明C
:
template <template <typename...> class C, typename T>
void foo(C<T> const&) { }
std::vector<int> v;
foo(v); // ok in both gcc and clang
Run Code Online (Sandbox Code Playgroud)