如何在不指定类名的情况下专门化模板
How to specialize a template without specifying a class name?
我想创建一个名为debug
的函数,输出一些关于对象的信息。我的系统包含许多不同类型的对象;其中一些包含其他对象。
using namespace std; // for brevity
struct dog {string name;};
struct human {string name; string address;};
struct line {list<human*> contents;};
struct pack {vector<dog*> contents;};
我希望函数输出参数的成员name
(如果它有),或者调试参数的contents
成员(如果它只有)。我想出了以下代码:
template <class T>
void debug(T object) // T here is a simple object like dog, human, etc
{
cout << object.name.c_str() << 'n';
}
// A helper function, not really important
template <class T>
void debug_pointer(T* object)
{
debug(*object);
}
void debug(pack object)
{
for_each(object.contents.begin(), object.contents.end(), debug_pointer<dog>);
}
void debug(line object)
{
for_each(object.contents.begin(), object.contents.end(), debug_pointer<human>);
}
这里,pack
和line
的代码几乎相同!我想避免多次编写相同的代码:
struct line {list<human*> contents; typedef human type;};
struct pack {vector<dog*> contents; typedef dog type;};
template <class T>
void debug(T object) // T here is a compound object (having contents)
{
for_each(object.contents.begin(), object.contents.end(), debug_pointer<T::type>);
}
但这种语法与"简单"对象的函数模板(具有相同的签名)相冲突。
如何重写代码?我不想重写第一部分(dog
、human
等的声明),因为我的程序的这一部分已经非常复杂,向其中添加一些东西(基类、成员函数等)只是为了调试似乎是不合适的。
基本代码可能如下所示:
template <typename T> void debug(T const & x)
{
debug_helper<T, has_name<T>::value>::print(x);
}
我们需要一个助手类:
template <typename, bool> struct debug_helper;
template <typename T> struct debug_helper<T, true>
{
static void print(T const & x) { /* print x.name */ }
};
template <typename T> struct debug_helper<T, false>
{
static void print(T const & x) { /* print x.content */ }
};
现在我们只需要一个SFINAE特性类has_name<T>
,以及一个打印容器的机制。这两个问题在漂亮的打印机代码中几乎一字不差地得到了解决。
将容器也作为模板参数:
template <template <typename> class Container, typename T>
void debug(Container<T> object)
{
for_each(object.contents.begin(), object.contents.end(), debug_pointer<T>);
}
顺便说一句,在大多数情况下,您可能希望通过常量引用而不是通过值传递(这需要复制整个向量/列表):
template <template <typename> class Container, typename T>
void debug(const Container<T>& object)
如果可以使用C++11,则可以使用decltype
根据内容确定T
:
template <typename T>
void debug(const T& object)
{
typedef decltype(*object.contents.front()) T;
for_each(object.contents.begin(), object.contents.end(), debug_pointer<T>);
}
当不能使用C++11时,GCC也有typeof
。
使用C++11、decltype
和SFINAE使事情变得简单:)
#include <string>
#include <vector>
#include <list>
#include <iostream>
#include <algorithm>
struct dog { std::string name; };
struct human { std::string name; std::string address; };
struct line { std::list<human*> contents; };
struct pack { std::vector<dog*> contents; };
template <typename T>
auto debug(T const& t) -> decltype(t.name, void(0)) {
std::cout << t.name << 'n';
}
template <typename T>
auto debug(T const* t) -> decltype(t->name, void(0)) {
if (t != 0) std::cout << t->name << 'n';
}
struct Debugger {
template <typename T>
void operator()(T const& t) { debug(t); }
};
template <typename C>
auto debug(C const& c) -> decltype(c.contents, void(0)) {
typedef decltype(c.contents) contents_type;
typedef typename contents_type::value_type type;
std::for_each(c.contents.begin(), c.contents.end(), Debugger());
}
int main() {
dog dog1 = { "dog1" }, dog2 = { "dog2" };
human h1 = { "h1" }, h2 = { "h2" };
line l; l.contents.push_back(&h1); l.contents.push_back(&h2);
debug(l);
}
在ideone的行动中,这产生了:
h1
h2
如预期:)
如果没有C++11,它需要一些小技巧,但原理保持不变,使用boost::enable_if
,您需要创建一个结构,该结构将根据name
和contents
的存在和可访问性引发编译错误。
当然,如果您简单地将结构本身中的方法连接起来,这一切都会更容易:)
您可以使用SFINAE来选择正在使用的过载。
我忘记了确切的细节,但您可以使用它来检测"内容"成员或"名称"成员的存在,然后基于此进行过载。
- 在没有太多条件句的情况下,我如何避免被零除
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 在未初始化映射的情况下,将值插入到映射的映射中
- 是默认情况下分配给char数组常量的值
- 为什么我不能在不创建字符串变量的情况下使用函数的字符串输出
- 如何在不产生任何垃圾的情况下获得C中的像素
- 在已经使用Git的情况下减少编译时间
- 为什么在Windows上的VS 2019和Clang 9中"size_t"在没有标题的情况下工作
- 如何在没有信号的情况下从C++执行QML插槽
- 如何在不知道向量大小的情况下输入向量内部的向量?
- 为什么在某些情况下不写入此文件?
- 是否可以防止在没有专门化的情况下使用C++模板
- 如何在没有参数的情况下专门化模板类
- 在不丢失功能的情况下通过重载来专门化类
- 如何在不指定类名的情况下专门化模板
- 为什么模板类的显式方法专门化可以在类内部没有原型声明的情况下工作
- 在这种情况下,是否可以在cpp文件中编写模板专门化?
- 如何在不重载比较操作符的情况下为std::max专门化自定义类型
- 在不支持基础模板的情况下,专门化概念模板
- 一个模板专门化用于pod,一个模板专门化用于类层次结构,在其他情况下出现错误