c++ - 模板类模板成员的模板特化

标签 c++ templates c++11 specialization

这可能只是语法问题。

所以我有这个模板类:

template <typename String, template<class> class Allocator>
class basic_data_object
{
  template<typename T>
  using array_container = std::vector<T, Allocator<T>>;
};

还有一个:

template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value
{
};

现在我想用第一个的内部 typedef array_container 为任何给定类型专门化第二个的 T 参数。

template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value
<String, Allocator,
typename basic_data_object<String, Allocator>::template array_container<T>>
{
};

但是当我将 std::vector 作为最后一个参数传递时,这种特化似乎不匹配。

如果我创建一个临时硬编码 typedef:

typedef basic_data_object<std::string, std::allocator<std::string>> data_object;

并将其用于特化,一切正常:

template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value
<String, Allocator,
data_object::template array_container<T>>
{
};

我错过了什么? :)


或者,使这项工作的最佳(最小/最干净)方法是什么?

最佳答案

C++ 标准在 [temp.class.spec.match] 第 2 段中说:

A partial specialization matches a given actual template argument list if the template arguments of the partial specialization can be deduced from the actual template argument list (14.8.2).

14.8.2 是 [temp.arg.deduct] 即描述函数模板的模板参数推导的子句。

如果你修改你的代码使用类似的函数模板并尝试调用它,你会看到参数无法推导:

template <typename String, typename T>
void deduction_test(String,
                    typename basic_data_object<String, std::allocator>::template array_container<T>)
{ }

int main()
{
  deduction_test(std::string{}, std::vector<int, std::allocator<int>>{});
}

(我删除了 Allocator 参数,因为无法将模板模板参数作为函数参数传递,并且在 basic_data_object 类型中它是非推导上下文,我认为它不会影响结果。 )

clang 和 GCC 都说他们无法推断 T这里。因此,部分特化将不匹配用作模板参数的相同类型。

所以我还没有真正回答这个问题,只是澄清了原因在模板参数推导的规则中,并显示了与函数模板中的推导等价。

在 14.8.2.5 [temp.deduct.type] 中,我们得到一个防止演绎的非演绎上下文列表,以及第 6 段中的以下规则:

When a type name is specified in a way that includes a non-deduced context, all of the types that comprise that type name are also non-deduced.

自从 basic_data_object<String, Allocator>在非推导上下文中(它是一个 nested-name-specifier,即出现在 :: 之前),表示类型 T也是非演绎的,这正是 Clang 和 GCC 告诉我们的。


使用您的临时硬编码 typedef 没有未推断的上下文,因此可以推断 T使用 deduction_test 成功函数模板:

template <typename String, typename T>
void deduction_test(String,
                    typename data_object::template array_container<T>)
{ }

int main()
{
  deduction_test(std::string{}, std::vector<int, std::allocator<int>>{}); // OK
}

因此,相应地,您的类模板部分特化可以在使用该类型时匹配。


如果不更改 get_data_object_value 的定义,我没有办法让它工作。 ,但如果这是一个选项,您可以消除推断 array_container 的需要type 而是使用一个 trait 来检测一个类型是否是你想要的类型,并专注于 trait 的结果:

#include <string>
#include <vector>
#include <iostream>

template <typename String, template<class> class Allocator>
class basic_data_object
{
public:
  template<typename T>
  using array_container = std::vector<T, Allocator<T>>;

  template<typename T>
    struct is_ac : std::false_type { };

  template<typename T>
    struct is_ac<array_container<T>> : std::true_type { };
};

template <typename String, template<class> class Allocator, typename T, bool = basic_data_object<String, Allocator>::template is_ac<T>::value>
struct get_data_object_value
{
};

template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value<String, Allocator, T, true>
{
  void f() { }
};

int main()
{
  get_data_object_value<std::string,std::allocator,std::vector<short>> obj;
  obj.f();
}

如果您想要几个类模板部分特化,这并不能真正扩展,因为您需要添加几个 bool带有默认参数的模板参数。

关于c++ - 模板类模板成员的模板特化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23362391/

相关文章:

c++ - boost::property_map在boost中是怎么实现的,怎么改

c++ - 如何在 C++ 中跟踪内存分配(尤其是新建/删除)

c++ - 尝试通过构造函数初始化 String 类。我得到奇怪的输出

C++ 将简单值转换为字符串

c++ - 指向数据类型的指针的类模板推导

c++ - OpenCV 配置问题

c++ - OpenGL 覆盖 Windows 的按钮

javascript - 在 Android Webview 中,我可以修改网页的 DOM 吗?

C++编译过程: Place for templates

c++ - 使用 typedef 的模板特化