c++模板:成员专业化问题

发布于 2024-08-26 12:14:48 字数 1973 浏览 10 评论 0原文

我正在尝试创建一个模板“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”成员的 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);
    }
};

并且我开始在实现相应的“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);
    }
};

此外,似乎我需要为 专业化实现 get 。关于如何解决这个问题有什么想法吗?

I am attempting to create a template "AutoClass" that create an arbitrary class with an arbitrary set of members, such as:

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;

By now I have an AutoClass with a working "set" member:

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

and I started to have problems implementing the corresponding "get". This approach doesn't compile:

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

Besides, it seems I need to implement get for the <nothing, nothing, nothing, nothing, nothing, nothing> specialization. Any Idea on how to solve this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

忘羡 2024-09-02 12:14:48

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

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

First of all, I prefer Boost.Fusion to Boost.Tuple as it supports a better mixin of template metaprogramming and runtime algorithms I think.

For example, I'd like to present you a little marvel:

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

It's really fun to use:

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

Well, I myself prefer indexing by classes than by indices, because I can convey meaning as well as adding type checking ;)

回忆追雨的时光 2024-09-02 12:14:48

我是否可以推荐使用 Boost 库的广泛(且经过良好测试和跨平台)模板魔法类集?听起来您要寻找的是 boost: :元组。任何时候只要你可以不编写自己的代码(尤其是在使用模板的复杂情况下),你就应该使用别人的代码。

Might I recommend using the Boost library's extensive (and well-tested and cross-platform) set of template-magicky classes? It sounds like what you're looking for is boost::tuple. Any time you can get away with not writing your own code—especially in a complicated situation with templates—you should use someone else's.

爱已欠费 2024-09-02 12:14:48

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

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

As others mentioned, you probably should be able to get where you want by reusing existing implementations from Boost or elsewhere.

If you would be doing something that can't be done using those or if you're curious:

  • try to keep the pseudo-variadic templates out of the implementation
  • use type-lists instead to allow for recursive meta-functions etc.
  • use pseudo-variadic templates as an interface if needed that forwards
    to the implementation
  • do as much at compile-time as possible, especially checks for indices etc.

A simple approach, utilizing MPL for convenience could look something like this:

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

Usage:

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

Keep in mind that this can still be wrapped with pseudo-variadic templates for end-user-convenience.

半葬歌 2024-09-02 12:14:48

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

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 的完整 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::get 因为 n 可能是 1093。

我在当前的界面中看不到解决这个问题的方法。如果将 n 放在模板参数中,则可以创建一个不会执行此操作的特殊情况。在这种情况下你不能。我认为您会遇到很多问题,因为您选择了这个很难解决的界面。例如,当 W 是 'int' 但 AutoClass::get(n-1) 返回 double 或更糟、完全不兼容的东西时会发生什么?

You need to implement for <nothing, nothing...> because of your base case. Consider:

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

Consider what happens when you call this function on a full AutoClass with an n of 5. It creates an autoclass with 5 members and calls with n = 4....and again until it reaches this point:

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...>
}

Sure, the call to this autoclass won't happen but the compiler has to compile that code anyway because you've told it to.

You also need to make an AutoClass<nothing,...>::get because n could be 1093.

I don't see a way out of this with your current interface. If you put the n in the template argument you could make a special case that wouldn't do this. In this case you cannot. I think you're going to run into a lot of issues because you've chosen this interface that are going to be rather difficult to solve. For example, what happens when W is 'int' but AutoClass::get(n-1) returns a double or worse, something totally incompatible?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文