2010-03-26 66 views
1

我试图创建一个模板“AUTOCLASS”与任意一组的成员,如创建一个任意等级:C++模板:与会员专业化问题

AutoClass<int,int,double,double> a; 
a.set(1,1); 
a.set(0,2); 
a.set(3,99.7); 
std::cout << "Hello world! " << a.get(0) << " " << a.get(1) << " " << a.get(3) << std::endl; 

现在我有一个AUTOCLASS用工作“设置”成员:

class nothing {}; 

template < typename T1 = nothing, typename T2 = nothing, typename T3 = nothing, 
      typename T4 = nothing, typename T5 = nothing, typename T6 = nothing> 
class AutoClass; 

template <> 
class AutoClass<nothing, nothing, nothing, 
       nothing, nothing, nothing> 
{ 
    public: 
    template <typename U> void set(int n,U v){} 
}; 

template < typename T1, typename T2, typename T3, 
      typename T4, typename T5, typename T6> 
class AutoClass: AutoClass<T2,T3,T4,T5,T6> 
{ 
    public: 
    T1 V; 
    template <typename U> void set(int n,U v) 
    { 
     if (n <= 0) 
      V = v; 
     else 
      AutoClass<T2,T3,T4,T5,T6>::set(n-1,v); 
    } 
}; 

我开始有问题实施相应的“得到”。这种方法不会编译:

template < typename T1, typename T2, typename T3, 
      typename T4, typename T5, typename T6> 
class AutoClass: AutoClass<T2,T3,T4,T5,T6> 
{ 
    public: 
    T1 V; 
    template <typename U> void set(int n,U v) 
    { 
     if (n <= 0) 
      V = v; 
     else 
      AutoClass<T2,T3,T4,T5,T6>::set(n-1,v); 
    } 
    template <typename W> W get(int n) 
    { 
     if (n <= 0) 
      return V; 
     else 
      return AutoClass<T2,T3,T4,T5,T6>::get(n-1); 
    } 
    template <> T1 get(int n) 
    { 
     if (n <= 0) 
      return V; 
     else 
      return AutoClass<T2,T3,T4,T5,T6>::get(n-1); 
    } 
}; 

而且,看来我需要实现获得的<nothing, nothing, nothing, nothing, nothing, nothing>专业化。任何关于如何解决这个问题的想法?

+0

谢谢大家,我会尝试使用Boost.Fusion来实现此目的。 – 2010-03-28 18:43:59

回答

3

首先,我更喜欢Boost.FusionBoost.Tuple,因为它支持我认为更好的模板元编程和运行时算法混合。

例如,我想向您介绍一个小奇迹:

struct Name {}; extern const Name name; 
struct GivenName {}; extern const GivenName givenName; 
struct Age {}; extern const Age age; 

class Person 
{ 
public: 
    template <class T> 
    struct value 
    { 
    typedef typename boost::fusion::result_of::at_key<data_type const,T>::type type; 
    }; 

    template <class T> 
    struct has 
    { 
    typedef typename boost::fusion::result_of::has_key<data_type,T>::type type; 
    }; 

    template <class T> 
    typename value<T>::type 
    get(T) { return boost::fusion::at_key<T>(mData); } 

    template <class T> 
    Person& set(T, typename value<T>::type v) 
    { 
    boost::fusion::at_key<T>(mData) = v; return *this; 
    }; 

private: 
    typedef boost::fusion::map < 
    std::pair<Name, std::string>, 
    std::pair<GivenName, std::string>, 
    std::pair<Age, unsigned short> 
    > data_type; 
    data_type mData; 
}; 

这真的很有趣的使用:

Person p; 
p.set(name, "Rabbit").set(givenName, "Roger").set(age, 22); 

好了,我自己的班级喜欢索引比指数,因为我可以传达意义以及添加类型检查;)

+1

我喜欢基于标签的索引+1。 – 2010-03-26 19:44:30

+0

使用Boost.Fusion集合来保存数据还有另一个好处>它就好像您获得了反射,因此您可以自动化打印(用于调试),序列化和反序列化,对消息的编码和解码等。 – 2010-03-28 11:48:20

3

我可以推荐使用Boost库的广泛的(以及经过充分测试和跨平台的)模板 - magicky类的设置吗?这听起来像你正在寻找的是boost::tuple。任何时候你都可以不用编写自己的代码—,特别是在模板复杂的情况下—你应该使用别人的。

+0

我知道boost :: tuple。不过,我想添加更多的方法到这个类模板,不在boost :: tuple上。所以我试图用这种方式来实现它。但我同意通常最好使用已知的库,而不是重新发明轮子。 – 2010-03-26 17:36:24

+0

您是否尝试过通过创建新函数来简单添加额外的功能来增强::元组?你在寻找什么额外的功能? – 2010-03-26 17:40:14

+0

也许你可以继承它或者添加额外的模板化函数来实现其他功能。 – 2010-03-26 17:42:16

0

您需要执行<没有,没有... >因为您的基本情况。试想一下:

template <typename W> W get(int n) 
{ 
    if (n <= 0) 
     return V; 
    else 
     return AutoClass<T2,T3,T4,T5,T6>::get(n-1); 
} 

考虑会发生什么,当你用5正呼吁全AUTOCLASS这种功能,它与5名成员创建了一个AUTOCLASS与N = 4调用... 。而直到再次做到了这一点:

template <typename W> W get(int n) // current autoclass is <T6,nothing,nothing...> 
{ 
    if (n <= 0) 
     return V; 
    else 
     return AutoClass<T2,T3,T4,T5,T6>::get(n-1); // this is <nothing, nothing...> 
} 

当然,这个autoclass的调用不会发生,但编译器必须编译该代码,因为你已经告诉它了。

您还需要作出AUTOCLASS <什么都没有,... > ::得到,因为n可以取是1093

我看不到出路的这与你当前界面。如果你把n放在模板参数中,你可以创建一个不会这样做的特例。在这种情况下,你不能。我认为你会遇到很多问题,因为你选择了这个界面将会很难解决。例如,当W是'int'但是AutoClass :: get(n-1)返回一个double或者更糟糕的东西时会发生什么,这是完全不相容的?

+0

是的,我意识到这个问题。也许你所说的将索引n放入模板中,比如'a.get <2>()'是一个好主意,但我不知道该怎么做。 – 2010-03-26 17:42:34

+0

您需要编写至少一个以上的元函数:return_type 。然后你的函数最终会得到签名:template < int n> typename return_type :: type get();使用到目前为止您使用的类似方法,但使用模板特化作为递归而不是函数递归。 – 2010-03-26 17:57:07

1

正如其他人提到的,您可能应该能够通过重用Boost或其他地方的现有实现来获得所需的位置。

如果你会做的东西,不能使用这些来完成,或者如果你很好奇:

  • 尽量保持伪可变参数模板出推行
  • 使用类型列表而是允许递归元函数等。
  • 如果需要使用伪可变参数模板作为一个接口转发 的实施
  • 做尽可能多的在编译时有可能,尤其是对指数等

一个简单的方法检查,使用MPL为方便起见,可能是这个样子:

template<class Types, size_t N> struct holder 
    // recursively derive from holder types: 
    : holder<Types, N-1> 
{ 
    typename boost::mpl::at_c<Types,N>::type value; 
}; 

// specialization that terminates the recursive derivation: 
template<class Types> struct holder<Types,0> { 
    typename boost::mpl::at_c<Types,0>::type value; 
}; 

template<class Types> 
class AutoClass 
    // recursively derive from holder types: 
    : holder<Types, boost::mpl::size<Types>::value-1>  
{ 
    enum { n = boost::mpl::size<Types>::value }; 
public: 
    template<size_t N, class U> void set(const U& u) { 
     // index check at compile time: 
     BOOST_STATIC_ASSERT((N < n)); 
     // cast to responsible holder base: 
     static_cast<holder<Types,N>*>(this)->value = u; 
    } 
    template<size_t N> typename boost::mpl::at_c<Types,N>::type get() const { 
     // index check at compile time: 
     BOOST_STATIC_ASSERT((N < n)); 
     // cast to responsible holder base: 
     return static_cast<const holder<Types,N>*>(this)->value; 
    } 
}; 

用法:

typedef boost::mpl::vector<int,std::string> Types; 
AutoClass<Types> a; 

a.set<0>(42); 
assert(a.get<0>() == 42); 
a.set<1>("abcde"); 
assert(a.get<1>() == "abcde"); 

请记住,为了最终用户的便利,这仍然可以使用伪变量模板进行包装。