-1

我想做一个函数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>);
}

但是这种语法与“简单”对象的函数模板冲突(具有相同的签名)。

我怎样才能重写我的代码?我不想重写第一部分(对 , 等的声明doghuman,因为我的程序的那部分已经非常复杂,并且只是为了调试而向其中添加东西(基类、成员函数等)似乎不合适。

4

4 回答 4

1

使容器也成为模板参数:

template <template <typename> class Container, typename T>
void debug(Container<T> object)
{
    for_each(object.contents.begin(), object.contents.end(), debug_pointer<T>);
}

顺便说一句,大多数情况下您可能希望通过 const 引用而不是按值传递(这需要复制整个向量/列表):

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>);
}

typeof当 C++11 无法使用时,GCC 也有。

于 2012-01-29T15:50:12.993 回答
1

基本代码可能如下所示:

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 trait 类has_name<T>和一个打印容器的机制。这两个问题在漂亮的打印机代码中几乎一字不差地解决了。

于 2012-01-29T16:17:47.550 回答
1

使用 C++11decltype和 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您需要创建一个结构,该结构将根据 and 的存在和可访问性引发编译name错误contents

当然,如果您只是简单地将结构本身中的方法连接起来,这一切都会更容易:)

于 2012-01-29T16:46:23.953 回答
0

您可以使用 SFINAE 选择正在使用的过载。

我忘记了确切的细节,但是您可以使用它来检测“内容”成员或“名称”成员的存在,然后基于此重载。

于 2012-01-29T16:06:44.697 回答