模板模板参数替换失败

时间:2019-03-09 23:27:47

标签: c++ templates c++17 variadic-templates template-deduction

我想要一个帮助函数来为我实例化一个类。当前它不能在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个错误。

3 个答案:

答案 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...),则需要将其放在最后一个位置,因此必须切换ContainerT(现在为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_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

答案 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