代码之家  ›  专栏  ›  技术社区  ›  chaos.ct

C++模板:成员专业化的问题

  •  1
  • chaos.ct  · 技术社区  · 14 年前

    我正在尝试创建一个模板“autoclass”,该模板创建一个具有任意成员集的任意类,例如:

    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;
    

    到目前为止,我有一个具有工作“set”成员的自动类:

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

    我开始在实现相应的“get”时遇到问题。这种方法不会编译:

    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> 专业化。你知道怎么解决这个问题吗?

    4 回复  |  直到 14 年前
        1
  •  3
  •   Matthieu M.    14 年前

    首先,我更喜欢 Boost.Fusion Boost.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);
    

    嗯,我自己更喜欢按类而不是按索引编制索引,因为我可以传达含义,还可以添加类型检查;)

        2
  •  3
  •   Seth Johnson    14 年前

    我是否可以推荐使用boost库的大量模板magicky类(以及经过良好测试和跨平台的模板magicky类)?听起来你要找的是 boost::tuple . 任何时候你都可以不写自己的代码,特别是在模板复杂的情况下,你应该使用别人的。

        3
  •  1
  •   Georg Fritzsche    14 年前

    正如其他人提到的,您可能应该能够通过重用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");
    

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

        4
  •  0
  •   Edward Strange    14 年前

    因为您的基本情况,您需要为<无,无…>实现。考虑:

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

    考虑在n为5的完整自动类上调用此函数时会发生什么情况。它创建了一个有5个成员的自动类,并调用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...>
    }
    

    当然,对这个自动类的调用不会发生,但是编译器无论如何都必须编译该代码,因为您已经告诉它了。

    您还需要制作一个自动类<Nothing,…>::get,因为n可以是1093。

    我看你现在的界面没办法解决这个问题。如果你把n放在模板参数中,你可以做一个特殊的情况,而不是这样。在这种情况下你不能。我认为你会遇到很多问题,因为你选择了这个界面,这将是相当困难的解决。例如,当w是'int'但autoclass::get(n-1)返回一个double或更糟的值时,会发生什么情况,这是完全不兼容的?