我想要一个帮助函数来为我实例化一个类。当前它不能在clang中编译(尽管它可以在gcc中编译工作),但是我也需要它在clang中工作。目前,我正在使用clang version 6.0.0-1ubuntu2
。
我不确定为什么它会失败,因为gcc能够检测到该类型。我尝试从this post开始做一些事情,并玩了一段时间,但我一直碰壁。 MCVE可用,或者您可以在coliru here上尝试使用它:
#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);
}
main.cpp:19:18:错误:没有匹配的函数来调用“ make_some_class”
auto stuff = make_some_class(ints); ^~~~~~~~~~~~~~~
main.cpp:12:13:注意:候选模板被忽略:替换失败[with T = int]:模板模板参数的模板参数与其对应的模板模板参数不同
inline auto make_some_class(const C<T>& container) { ^
产生了1个错误。
答案 0 :(得分:3)
建议:尝试
#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);
}
我的意思是……我想问题是std::vector
不是一个接收一个类型模板参数的容器;这是一个接收 two 类型模板参数的容器(第二个具有默认类型:std::allocator<T>
,其中T
是第一个)。
因此,建议是:使SomeClass
更灵活,并能够接收带有可变参数模板类型列表的容器
template <typename...> typename Container
和相应的模板类型列表
typename ... Ts
如果您想要一个可变参数列表(Ts...
),则需要将其放在最后一个位置,因此必须切换Container
和T
(现在为Ts...
)的位置):在Container
之前和之后的Ts...
template <template <typename...> typename Container, typename ... Ts>
struct SomeClass {
SomeClass(const Container<Ts...>& c) {
}
};
不是严格要求的,但是为了统一起见,我建议以相同的方式重写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);
}
答案 1 :(得分:2)
正如comment和max66中已经建议的那样,std::vector
具有两个模板参数,value_type
和allocator_type
。
在C ++ 17之前,在这种情况下,我们必须明确地将这两个参数都写为模板模板参数,这在C ++ 17中也适用:
template <typename T, template <typename V, typename Allocator = std::allocator<T>> typename Container>
struct SomeClass {
SomeClass(const Container<T>& c) {
}
};
template <typename T, template <typename V, typename Allocator = std::allocator<T>> typename C>
inline auto make_some_class(const C<T>& container) {
return SomeClass<T, C>(container);
}
顺便说一句,从C ++ 17开始,"Matching template template parameters to compatible arguments" [P0522R0
]被接受,并且如果您使用的是C ++ 17,则您的代码正确。
但是Clang仍默认禁用此新功能,并且编译标志-frelaxed-template-template-args
启用此功能。
答案 2 :(得分:2)
让我们简化为:
template <template <typename> class C, typename T>
void foo(C<T> const&) { }
std::vector<int> v;
foo(v);
您会发现gcc可以编译,但是clang不能。两者的原因都很有趣。
首先,回想一下std::vector
是一个使用两个模板参数的类模板:
template <typename T, typename Alloc = std::allocator<T>>
class vector { ... };
为什么gcc认为匹配template <typename> class C
的模板参数只有一个一个?因为P0522R0导致规则更改。这很有意义-我们可以像在普通代码中使用vector
一样使用一个类型参数,因此它应该能够匹配这样的模板参数。
现在,为什么clang认为vector
不匹配?因为他们明确地选择不采用此规则。来自their docs:
(10):尽管是缺陷报告的解决方案,但是默认情况下,所有语言版本均禁用此功能,并且可以在Clang 4及更高版本中使用标志
-frelaxed-template-template-args
明确启用此功能。对标准的更改缺少模板部分排序的相应更改,从而导致合理且先前有效的代码存在歧义错误。该问题有望很快得到解决。
也就是说,它可能break code。
当然,您可能只想知道如何解决它。只需更改模板模板参数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