【问题标题】:C++ template partial specialization - specializing one member function onlyC++ 模板部分特化 - 只特化一个成员函数
【发布时间】:2009-11-18 17:43:13
【问题描述】:

碰到另一个模板问题:

问题:对于对象是指针的情况,我想部分专门化一个容器类 (foo),并且我只想专门化删除方法。应该是这样的:

库代码

template <typename T>
class foo
{
public:
    void addSome    (T o) { printf ("adding that object..."); }
    void deleteSome (T o) { printf ("deleting that object..."); }
};

template <typename T>
class foo <T *>
{
public:
    void deleteSome (T* o) { printf ("deleting that PTR to an object..."); }
};

用户代码

foo<myclass> myclasses;
foo<myclass*> myptrs;

myptrs.addSome (new myclass());

这导致编译器告诉我 myptrs 没有名为 addSome 的方法。 为什么?

谢谢。


Solution

基于 tony 的回答,这里是完全可编译的东西

template <typename T>
class foobase
{
public:
    void addSome    (T o) { printf ("adding that object..."); }
    void deleteSome (T o) { printf ("deleting that object..."); }
};


template <typename T>
class foo : public foobase<T>
{ };

template <typename T>
class foo<T *> : public foobase<T *>
{
public:
    void deleteSome (T* o) { printf ("deleting that ptr to an object..."); }
};

用户

foo<int>    fi;
foo<int*>   fpi;

int         i = 13;

fi.addSome (12);            
fpi.addSome (&i);

fpi.deleteSome (12);        // compiler-error: doesnt work
fi.deleteSome (&i);         // compiler-error: doesnt work
fi.deleteSome (12);         // foobase::deleteSome called
fpi.deleteSome (&i);        // foo<T*>::deleteSome called

【问题讨论】:

  • 错误的解决方案。假设我们使用 foo。假设class foobase 的某个函数调用了deleteSome。会调用什么函数?正确的! class foobase 的 deleteSome(T) 而不是 deleteSome(T *)。
  • @Malistov:我认为关键是不应该使用 foobase。它只是为了使黑客成为可能。
  • @splicer,但如果您调用deleteSome 以外的函数,它隐式使用。到那时,如果被调用函数调用deleteSome,它将永远无法到达派生类中的函数。
  • @JohannesSchaub-litb 如果基类指定函数是虚拟的(并且没有实例化)怎么办?我以前见过这个问题/设计模式:它有名字吗?

标签: c++ templates partial-specialization


【解决方案1】:

第二个解决方案(正确的一个)

template <typename T>
class foo
{
public:
    void addSome    (T o) { printf ("adding that object..."); } 
    void deleteSome(T o) { deleteSomeHelper<T>()(o); }
protected:
    template<typename TX> 
    struct deleteSomeHelper { void operator()(TX& o) { printf ("deleting that object..."); } };
    template<typename TX> 
    struct deleteSomeHelper<TX*> { void operator()(TX*& o) { printf ("deleting that PTR to an object..."); } };
};

此方案根据Core Issue #727有效。


第一个(不正确的)解决方案:(保留这个作为 cmets 引用它)

你不能只专攻部分课程。在您的情况下,最好的方法是重载函数deleteSome,如下所示:

template <typename T>
class foo
{
public:
    void addSome    (T o) { printf ("adding that object..."); }
    void deleteSome (T o) { printf ("deleting that object..."); }
    void deleteSome (T* o) { printf ("deleting that object..."); }
};

【讨论】:

  • 嗯,这是一个很好的建议,但它也使以下情况能够无错误地编译: - foo fi;诠释我; fi.deleteSome (&i);它当然应该只能“deleteSome” int 类型的地方
  • @Kirill。这是行不通的。假设我们使用 foo。那么 foo 有 3 个函数:addSome (myclass * o)deleteSome (myclass * o)deleteSome (myclass ** o)。最后一个函数永远不会被调用。
  • 我现在去购物。但是我要给您留个便条:您不能将成员模板的显式特化放入类主体中。你也不能把它放在外面,因为这不起作用:template&lt;typename T&gt; template&lt;&gt; void foo&lt;T&gt;::deleteSomeHelper&lt;false&gt;(T &amp;o) { }(因为template&lt;&gt;之前可能没有任何模板参数子句)。
  • @litb,现在我明白你在说什么了。固定。
  • +1 表示第二种解决方案。看起来不错。请参阅下面的使用 boost::remove_pointer 的想法,避免额外的功能。
【解决方案2】:

另一种解决方案。使用辅助功能deleteSomeHelp

template <typename T>
class foo {
 public:    
   void addSome    (T o) { printf ("adding that object..."); 
   template<class R>
   void deleteSomeHelp (R   o) { printf ("deleting that object..."); }};
   template<class R>
   void deleteSomeHelp (R * o) { printf ("deleting that PTR to an object..."); }};
   void deleteSome (T o) { deleteSomeHelp(o); }
}    

【讨论】:

  • 疯狂的东西 :),但我在这里面临同样的问题,因此 foo 可以调用 deleteSome 并给出一个指针。
  • +1 看起来像我会立即使用的那个。它更简单,可以完成工作。
【解决方案3】:

我还没有看到这个解决方案,使用boost的enable_ifis_sameremove_pointer在一个类中获取两个函数,没有任何继承或其他麻烦。

仅使用remove_pointer 的版本见下文。

#include <boost\utility\enable_if.hpp>
#include <boost\type_traits\is_same.hpp>
#include <boost\type_traits\remove_pointer.hpp>

template <typename T>
class foo
{
public:
    typedef typename boost::remove_pointer<T>::type T_noptr;

    void addSome    (T o) { printf ("adding that object..."); }

    template<typename U>
    void deleteSome (U o, typename boost::enable_if<boost::is_same<T_noptr, U>>::type* dummy = 0) { 
        printf ("deleting that object..."); 
    }
    template<typename U>
    void deleteSome (U* o, typename boost::enable_if<boost::is_same<T_noptr, U>>::type* dummy = 0) { 
        printf ("deleting that PTR to that object..."); 
    }
};

简化版是:

#include <cstdio>
#include <boost\type_traits\remove_pointer.hpp>

template <typename T>
class foo
{
public:
    typedef typename boost::remove_pointer<T>::type T_value;

    void addSome    (T o) { printf ("adding that object..."); }

    void deleteSome (T_value& o) { // need ref to avoid auto-conv of double->int
        printf ("deleting that object..."); 
    }

    void deleteSome (T_value* o) { 
        printf ("deleting that PTR to that object..."); 
    }
};

它适用于 MSVC 9:(注释掉了错误的行,因为它们是不正确的,但有利于测试)

void main()
{
   foo<int> x;
   foo<int*> y;

   int a;
   float b;

   x.deleteSome(a);
   x.deleteSome(&a);
   //x.deleteSome(b); // doesn't compile, as it shouldn't
   //x.deleteSome(&b);
   y.deleteSome(a);
   y.deleteSome(&a);
   //y.deleteSome(b);
   //y.deleteSome(&b);
}

【讨论】:

    【解决方案4】:

    为单个函数deleteSome创建基类

    template<class T>
    class base {
    public:
      void deleteSome (T o) { printf ("deleting that object..."); }
    }
    

    进行部分特化

    template<class T>
    class base<T*> {
    public:
      void deleteSome (T * o) { printf ("deleting that PTR to an object..."); }
    }
    

    使用你的基类

    template <typename T>
    class foo : public base<T> {
     public:    
       void addSome    (T o) { printf ("adding that object..."); 
    }    
    

    【讨论】:

      【解决方案5】:

      您可以使用继承来使其工作:

      template <typename T>
      class foobase
      {
      public:
          void addSome    (T o) { printf ("adding that object..."); }
          void deleteSome (T o) { printf ("deleting that object..."); }
      };
      
      template <typename T>
      class foo : public foobase<T>
      { };
      
      template <typename T>
      class foo <T *> : public foobase<T>
      {
      public:
          void deleteSome (T* o) { printf ("deleting that PTR to an object..."); }
      };
      

      【讨论】:

      • 谢谢托尼,我想我会接受你的解决方案。
      • 此解决方案不可扩展。假设您要添加新函数 doSomething(T o) 并专门针对对象为 std::list. 的情况
      • 这个解决方案也是错误的。假设我们使用 fooclass foobase 的任何函数都不能调用deleteSome,因为将调用deleteSome (T) 而不是deleteSome (T*)
      • 嗯,不应该是class foo&lt;T*&gt; : public foobase&lt;T*&gt;(注意第二个*)吗?
      • Kirill 的回答错过了专业化的用途之一,即为您自己的新类型专门化现有类(没有修改现有类的能力)。托尼的解决方案是最通用的。
      猜你喜欢
      • 2012-12-15
      • 1970-01-01
      • 2013-02-28
      • 1970-01-01
      • 2011-08-22
      • 1970-01-01
      • 2013-09-24
      • 2011-12-25
      相关资源
      最近更新 更多