模板模板参数的替换失败

Substitution failure for template template argument

我想要一个辅助函数来为我实例化一个 class。目前它不能在 clang 中编译(尽管它在 gcc 中编译工作),但我也需要它在 clang 中工作。目前我正在使用 clang version 6.0.0-1ubuntu2.

我不确定为什么会失败,因为 gcc 能够检测到类型。我试着做 this post and playing around with it for a while but I keep running into a wall. MCVE available, or you can try it on 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: error: no matching function for call to 'make_some_class'

   auto stuff = make_some_class(ints);  

                ^~~~~~~~~~~~~~~

main.cpp:12:13: note: candidate template ignored: substitution failure [with T = int]: template template argument has different template parameters than its corresponding template template parameter

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

            ^

1 error generated.

建议:试试

#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 不是接收 one 类型模板参数的容器;它是一个接收 two 类型模板参数的容器(第二个具有默认类型:std::allocator<T>,其中 T 是第一个)。

所以建议是:使 SomeClass 更灵活,并能够接收带有可变参数模板类型列表的容器

template <typename...> typename Container

对应的模板类型列表

typename ... Ts

如果你想要一个可变参数列表(Ts...),你需要它在最后一个位置,所以你必须切换 ContainerT 的位置(现在 Ts...): 在 Container 之前和可变参数列表之后 Ts...

template <template <typename...> typename Container, typename ... Ts>
struct SomeClass {
    SomeClass(const Container<Ts...>& c) {
    }
};

并非严格要求,但为了统一起见,我建议以相同的方式重写 make_some_class()(显然,在模板参数列表中的 Ts... 之前传递 C)。

template <template <typename...> typename C, typename ... Ts>
inline auto make_some_class(const C<Ts...>& container) {
    return SomeClass<C, Ts...>(container);
}

正如评论和 max66 中已经建议的那样,std::vector 有两个模板参数,value_typeallocator_type。 在 C++17 之前,在这种情况下,我们必须显式地将两个参数写为模板模板参数,这在 C++17 中也适用:

DEMO

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 启用此功能。

DEMO

让我们将其简化为:

template <template <typename> class C, typename T>
void foo(C<T> const&) { }

std::vector<int> v;
foo(v);

您会发现 gcc 可以编译,但 clang 不会。两者的原因都很有趣。

首先,回想一下 std::vector 是一个 class 模板,它采用 两个 模板参数:

template <typename T, typename Alloc = std::allocator<T>>
class vector { ... };

为什么 gcc 认为匹配 template <typename> class C - 一个只有 一个 模板参数的模板?因为规则因 P0522R0 而改变。这是有道理的——我们可以使用 vector 就像它在普通代码中有一个类型参数一样,所以它应该能够匹配这样一个模板参数。

现在,为什么 clang 认为 vector 匹配?因为他们明确选择不采用此规则。来自 their docs:

(10): Despite being the resolution to a Defect Report, this feature is disabled by default in all language versions, and can be enabled explicitly with the flag -frelaxed-template-template-args in Clang 4 onwards. The change to the standard lacks a corresponding change for template partial ordering, resulting in ambiguity errors for reasonable and previously-valid code. This issue is expected to be rectified soon.

也就是说,它可能


当然,您可能只是想知道如何修复它。只需更改模板模板参数的声明 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