首页 > 解决方案 > 模板模板参数的显式匹配

问题描述

考虑这个函数:

template<template<class, class> class C, class T, class Alloc>
void foo(C<T, Alloc>& container) {
    std::cout << container.size() << std::endl;
}

函数foo()接受 a std::vector<T, Alloc>,但它std::map<Key, T, Compare, Allocator>在 C++17 中也接受,因为CompareAllocator具有默认值。笔记std::map在 C++14 中失败。

我怎样才能foo()只接受只有 2 个模板参数的模板,所以它std::map在 C++17 中失败了?

标签: c++c++14c++17template-argument-deductiontemplate-templates

解决方案


我怎样才能让 foo 只接受只有 2 个模板参数的模板,所以它在 C++17 中因 std::map 而失败?

如果您想避免foo()接受接受三个或更多模板参数的容器(因此失败std::map)相对简单:您可以遵循 rtpax 的建议,或者,给定一个自定义类型特征,说明一个类型是否基于两种类型的接受容器

template <typename>
struct accept2 : std::false_type
 { };

template <template <typename...> class C, typename X, typename Y>
struct accept2<C<X, Y>> : std::true_type
 { };

以及三接受容器的类似类型特征

template <typename>
struct accept3 : std::false_type
 { };

template <template <typename...> class C, typename X, typename Y, typename Z>
struct accept3<C<X, Y, Z>> : std::true_type
 { };

foo()仅当推导的类型接受两种但不接受三种类型时,您才能启用 SFINAE

template <typename C>
std::enable_if_t<accept2<C>{} && !accept3<C>{}> foo (C const & container)
 { }

但是这个解决方案(以及 rtpax 解决方案)有一​​个问题:如果容器在两个模板类型之前和一个(或多个)具有默认值的模板非类型参数之后接收呢?

还是两种模板类型和一个(或多个)具有默认值的模板模板参数?也许有不同的签名?

您可以添加accept3特化来识别其他情况,但类型、非类型和模板模板参数的无限组合,在前两个模板类型之后具有默认值。所以你必须编写无限特化来拦截所有情况。

这有点不切实际。

而且我怀疑(在 C++17 中)没有实用的解决方案。

无论如何,一个完整的编译示例(避免三个或更多模板类型容器)如下

#include <map>
#include <vector>
#include <type_traits>

template <typename>
struct accept2 : std::false_type
 { };

template <template <typename...> class C, typename X, typename Y>
struct accept2<C<X, Y>> : std::true_type
 { };

template <typename>
struct accept3 : std::false_type
 { };

template <template <typename...> class C, typename X, typename Y, typename Z>
struct accept3<C<X, Y, Z>> : std::true_type
 { };

template <typename C>
std::enable_if_t<accept2<C>{} && !accept3<C>{}> foo (C const &)
 { }

int main()
 {
   std::vector<int> v;
   std::map<int,int> m;

   foo(v);   // compile
   //foo(m); // compilation error
 }

推荐阅读