模板模板参数的替换失败

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”

   auto stuff = make_some_class(ints);  

                ^~~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)

main.cpp:12:13: 注意: 候选模板被忽略: 替换失败 [with T = int]: 模板模板参数的模板参数与其对应的模板模板参数不同

inline auto make_some_class(const C<T>& container) {

            ^
Run Code Online (Sandbox Code Playgroud)

产生了 1 个错误。

max*_*x66 5

建议:尝试

#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...) 你需要它在最后一个位置所以你必须切换ContainerT(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)


Bar*_*rry 5

让我们将其简化为:

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)