【问题标题】:C++ specialization of template function inside template class模板类中模板函数的 C++ 特化
【发布时间】:2011-06-27 01:22:40
【问题描述】:

用于特化模板类中的模板函数的 C++ 语法是什么?例如,考虑我有以下两个类及其用法。我希望能够为不同类型提供方法 X::getAThing() 的专门实现。例如:int、std::string、任意指​​针或类等

template <class c1> class X {
public:
   template<typename returnT> returnT getAThing(std::string param);
   static std::string getName();
private:
   c1 theData;
};

// This works ok...
template <class c1> std::string X<c1>::getName() {
   return c1::getName();
}

// This blows up with the error:
// error: prototype for 'int X<c1>::getAThing(std::string)' does not match any in class 'X<c1>'
template <class c1> template <typename returnT> int X<c1>::getAThing(std::string param) {
   return getIntThing(param); // Some function that crunches on param and returns an int.
}

// More specialized definitions of getAThing() for other types/classes go here...

class Y {
public:
   static std::string getName() { return "Y"; }
};

int main(int argc, char* argv[])
{
   X<Y> tester;
   int anIntThing = tester.getAThing<int>(std::string("param"));
   cout << "Name: " <<  tester.getName() << endl;
   cout << "An int thing: " << anIntThing << endl;
}

至少一个小时以来,我一直在尝试猜测专业化的正确语法,但无法弄清楚任何可以编译的东西。任何帮助将不胜感激!

【问题讨论】:

    标签: c++ templates syntax template-specialization


    【解决方案1】:

    AFAIK(标准专家可以纠正我),如果不专门化类本身,您就无法专门化类模板的模板化函数...

    即以下我认为会起作用:

    template <> template <> int X<Y>::getAThing<int>(std::string param) {
       return getIntThing(param); // Some function that crunches on param and returns an int.
    }
    

    【讨论】:

    • 我刚刚得出了同样的结论。
    • 我从 gcc 收到的错误消息表明您可能是正确的。虽然很难说,因为模板经常会导致奇怪和难以理解的错误消息。实际上,在 gcc 中进行测试确实有效。我会说你是对的,即使找到标准所说的确切位置是一项有趣的业务。
    • @Omnifarious,我明白你的意思,我今天被这个问题难住了大约一个小时:expected primary-expression before ‘&gt;’!?!原来我在调用类模板中的函数模板时丢失了关键字template,现在使用奇怪的语法:some_obj.template foo&lt;bar&gt;(); - wtf?谁认为这是一个聪明的主意?!?
    • @Omnifarious: 14.7.3p18: “在类模板的成员或出现在命名空间范围内的成员模板的显式特化声明中,成员模板和它的一些封闭类模板可能会保留未特化,除非声明不应显式特化类成员模板,如果其封闭类模板也未显式特化。"
    • 我认为我的英语掌握得很好,但正是这样的陈述让我眼花缭乱,我避免阅读标准的原因......
    【解决方案2】:

    C++ 没有函数模板的偏特化概念。但是,您可以通过函数重载获得与完全专业化相同的效果。

    我假设你有这样的东西,这确实是唯一的方法之一。

    template<class TYPE>
    class MyInterface {
    public:
        template<class RETURN>
        RETURN myFunction(RETURN& ref, ....);
    };
    

    在这种情况下,您可以通过声明具有所需类型的普通成员函数来专门化“myFunction()”。 C++ 的函数重载规则应该给你你想要的,例如

    template<class TYPE>
    class MyInterface {
    public:
        template<class RETURN>
        RETURN myFunction(RETURN& ref, ....);
    
        // String specialization
        std::string myFunction(std::string& ref, ...);
    };
    

    编译器将在适当的地方使用“std::string”函数,并且可能永远不会使用内部模板。

    【讨论】:

    • 我认为这不适用于非类型模板参数。
    【解决方案3】:

    所以,我采用不同的方法来回答您的问题。我将从做你想做的事情开始,并且有效。然后也许我们可以弄清楚如何将它置换成更接近你真正想要的东西:

    #include <string>
    #include <iostream>
    
    int getIntThing(const ::std::string &param);
    
    template <typename returnT>
    returnT getThingFree(const ::std::string &param);
    
    template <>
    int getThingFree<int>(const ::std::string &param)
    {
       return getIntThing(param);
    }
    
    // More specialized definitions of getAThing() for other types/classes
    // go here...
    
    template <class c1> class X {
    public:
       template<typename returnT> returnT getAThing(std::string param);
       static std::string getName();
    private:
       c1 theData;
    };
    
    // This works ok...
    template <class c1> std::string X<c1>::getName() {
       return c1::getName();
    }
    
    // This also works, but it would be nice if I could explicitly specialize
    // this instead of having to explicitly specialize getThingFree.
    template <class c1>
    template <class RT>
    RT X<c1>::getAThing(std::string param) {
       // Some function that crunches on param and returns an RT.
       // Gosh, wouldn't it be nice if I didn't have to redirect through
       // this free function?
       return getThingFree<RT>(param);
    }
    
    class Y {
    public:
       static std::string getName() { return "Y"; }
    };
    
    int main(int argc, char* argv[])
    {
       using ::std::cout;
       X<Y> tester;
       int anIntThing = tester.getAThing<int>(std::string("param"));
       cout << "Name: " <<  tester.getName() << '\n';
       cout << "An int thing: " << anIntThing << '\n';
    }
    

    这是另一种可行的想法,并不完全是您想要的,但更接近。我想你自己已经想到了。它使用类型推导的方式也相当丑陋。

    #include <string>
    #include <iostream>
    
    template <class c1> class X;
    
    int getIntThing(const ::std::string &param)
    {
       return param.size();
    }
    
    // You can partially specialize this, but only for the class, or the
    // class and return type. You cannot partially specialize this for
    // just the return type. OTOH, specializations will be able to access
    // private or protected members of X<c1> as this class is declared a
    // friend.
    template <class c1>
    class friendlyGetThing {
     public:
       template <typename return_t>
       static return_t getThing(X<c1> &xthis, const ::std::string &param,
                                return_t *);
    };
    
    // This can be partially specialized on either class, return type, or
    // both, but it cannot be declared a friend, so will have no access to
    // private or protected members of X<c1>.
    template <class c1, typename return_t>
    class getThingFunctor {
     public:
       typedef return_t r_t;
    
       return_t operator()(X<c1> &xthis, const ::std::string &param) {
          return_t *fred = 0;
          return friendlyGetThing<c1>::getThing(xthis, param, fred);
       }
    };
    
    template <class c1> class X {
    public:
       friend class friendlyGetThing<c1>;
    
       template<typename returnT> returnT getAThing(std::string param) {
          return getThingFunctor<c1, returnT>()(*this, param);
       }
       static std::string getName();
    private:
       c1 theData;
    };
    
    // This works ok...
    template <class c1> std::string X<c1>::getName() {
       return c1::getName();
    }
    
    class Y {
    public:
       static std::string getName() { return "Y"; }
    };
    
    template <class c1>
    class getThingFunctor<c1, int> {
     public:
       int operator()(X<c1> &xthis, const ::std::string &param) {
          return getIntThing(param);
       }
    };
    
    // More specialized definitions of getAThingFunctor for other types/classes
    // go here...
    
    int main(int argc, char* argv[])
    {
       using ::std::cout;
       X<Y> tester;
       int anIntThing = tester.getAThing<int>(std::string("param"));
       cout << "Name: " <<  tester.getName() << '\n';
       cout << "An int thing: " << anIntThing << '\n';
    }
    

    我建议在半私有实用程序命名空间中声明 getThingFunctorfriendlyGetThing

    【讨论】:

    • 这可能是唯一有成功机会的方法。我尝试使用嵌套的辅助类,因为类专业化的规则与函数不同,但没有乐趣。 Nit:最后的评论不应该是“getThingFree 的更专业的定义”,而且他们必须超越?
    • @Ben Voigt - 您的 nit 已被注意到并得到照顾。 :-)
    • 哇,我希望语言不会强迫我做如此恶心的事情。正如我在其他地方提到的,不必专门化 c1 类和 returnT 类的每个组合会很好。
    • @Ogre Psalm33 - 那么,getAThing 的任何实现都需要访问class X&lt;c1&gt; 的私有部分吗?
    • @Omnifarious - 好吧,看看我的“真实”实现,它目前正在访问受保护的成员,但我想我可以调整实现并消除对受保护成员的依赖。跨度>
    【解决方案4】:

    出于好奇,这可能是我将在自己的代码中采用的解决方案。这与Omnifarious 的答案略有不同,无需额外的课程。我仍然给Omnifarious 提供道具,因为他做了大部分的腿部工作:

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    // IMPORTANT NOTE: AdaptingFunctor has no access to the guts of class X!
    // Thus, this solution is somewhat limited.
    template<typename t1> class AdaptingFunctor {
    public:
       t1 operator() (string param);
    };
    
    // Can specialize AdaptingFunctor for each type required:
    template<> int AdaptingFunctor<int>::operator() (string param)
    {
       return param.size(); // <=== Insert required type-specific logic here
    }
    
    // Additional specializations for each return type can go
    // here, without requiring specialization of class c1 for X...
    
    
    template <class c1> class X {
    public:
       template<typename returnT>  returnT getAThing(std::string param)
          {
         AdaptingFunctor<returnT> adapter;
         return adapter(param);
          }
       static std::string getName();
    private:
       c1 theData;
    };
    
    // Template definition of class method works ok...
    template <class c1> std::string X<c1>::getName() {
       return c1::getName();
    }
    
    class Y {
    public:
       static std::string getName() { return "Y"; }
    };
    
    
    int main(int argc, char* argv[])
    {
       X<Y> tester;
       int anIntThing = tester.getAThing<int>(std::string("param"));
       cout << "Name: " <<  tester.getName() << endl;
       cout << "An int thing: " << anIntThing << endl;
    }
    

    【讨论】:

      【解决方案5】:

      试试

      template <>
      template <class T>
      int X<T>::template getAThing<int>(std::string param)
      {
         return getIntThing(param);
      }
      

      这仍然没有编译,但它比你更接近。

      我认为你不能对成员进行专门化。您必须先指定类模板的特定特化,然后才能开始对其成员进行特化。

      【讨论】:

      • 如果颠倒模板括号的顺序会怎样?即template &lt;class T&gt; template&lt;&gt;。更有意义,因为模板函数在模板类内部,所以它的模板声明应该嵌套在类的声明中。值得一试。
      • @Alexander Kondratskiy - 我试过了。还是不行。虽然有很多不同的错误。 :-)
      • 嗯,有趣的想法。是的,如果我必须为(例如)类 Y 和 int 专门化函数,这给我留下了很多潜在的函数组合来定义。如果你的预感是对的,Ben,我可能不得不回过头来做一些事情,比如使用外部函子类实现 getAThing(),并专门针对 getAThing() 的定义不依赖于类 c1 的信息的情况。
      • 这没有任何意义。 'X' 被专门化为 'T' 一个嵌套的模板参数。类型“X”与原始主模板无关,没有定义也没有成员。由于“getAThing”不是该类的成员,因此您无法将其专门用于该类。
      • @Richard:你错了 X&lt;T&gt; 是一个空的专业。失败的原因是您必须先专门化该类,然后才能专门化成员,而事实并非如此。但是,既然您坚持要投反对票,请随意。
      【解决方案6】:

      这是迄今为止我见过的最简单、最简单的方法:

      template <class T1>
      struct MyClass {
        template <class T2>
        void MyFunction();
      };
      
      template <class T1>
      template <class T2>
      void MyClass<T1>::MyFunction() {  // NOTE:  NO T2 on this line.
        // Code goes here
      }
      

      【讨论】:

      • OP 希望每个 'T2' 使用不同的函数体(在句法意义上)。您的示例对所有类型的 T2 使用相同的函数体。
      • 理查德·科登是正确的。我们希望能够针对许多不同的类或类型专门化 MyFunction 的主体,其中(这是重要的部分)T2 可以是任何基本类型。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多