理解(简单?)C++部分模板专业化

Dan*_*Dan 20 c++ templates template-specialization

注意:这似乎是一个问题的转贴:C++ - 重载模板化的类方法,并对该方法进行了部分说明

我已经将C++模板专业化的问题归结为一个简单的案例.

它由一个简单的2参数模板类组成Thing,我想专门Thing<A,B>::doSomething()研究它B=int.

#include <cstdio>

//
// A 3-parameter template class.
//
template <class A, class B>
class Thing
{
public:
    Thing(A a, B b) : a_(a), b_(b) {}
    B doSomething();
private:
    A a_;
    B b_;
};

//
// The generic case works as expected.
//
template <class A, class B>
B Thing<A,B>::doSomething()
{
    return b_;
}

//
// This specialization does not work!
//
template <class A>
int Thing<A,int>::doSomething()
{
    return b_+1;
}

int main( int argc, char** argv )
{
    // Setup our thing.
    Thing<double,int> thing(1.0,2);
    // This doesn't compile - but works with the generic case.
    printf("Expecting 3, and getting %i\n", thing.doSomething());
    // Clean up.
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,g++退出时出现错误:

partial_specialization.cpp:30: error: invalid use of incomplete type ‘class Thing<A, int>’
partial_specialization.cpp:8: error: declaration of ‘class Thing<A, int>’
Run Code Online (Sandbox Code Playgroud)

clang++编译器是更冗长了一点,但有相同的问题:

partial_specialization.cpp:30:19: error: nested name specifier 'Thing<A, int>::' for declaration does not
      refer into a class, class template or class template partial specialization
int Thing<A,int>::doSomething()
    ~~~~~~~~~~~~~~^
partial_specialization.cpp:32:12: error: use of undeclared identifier 'b_'
    return b_+1;
           ^
2 errors generated.
Run Code Online (Sandbox Code Playgroud)

我已阅读并理解不允许对函数进行部分模板特化 - 但我认为Thing在这种情况下我部分专门针对类.

有任何想法吗?

我做了什么:根据接受的答案提供的链接确定的解决方法:

template< class T >
inline T foo( T const & v ) { return v; }

template<>
inline int foo( int const & v ) { return v+1; }

//
// The generic case works as expected.
//
template <class A, class B>
B Thing<A,B>::doSomething()
{
    return foo(b_);
}
Run Code Online (Sandbox Code Playgroud)

Naw*_*waz 42

标准不允许对函数模板进行部分特化,无论是成员函数模板还是独立函数模板:

template<typename T, typename U> void f() {} //okay  - primary template
template<typename T> void f<T,int>() {}      //error - partial specialization
template<> void f<unsigned char,int>() {}    //okay  - full specialization
Run Code Online (Sandbox Code Playgroud)

但是你可以部分地专门化类模板本身.你可以这样做:

template <class A>
class Thing<A,int>  //partial specialization of the class template
{
    //..
    int doSomething();
};

template <class A>
int Thing<A,int>::doSomething()  { /* do whatever you want to do here */ }
Run Code Online (Sandbox Code Playgroud)

请注意,当您部分地专门化类模板时,成员函数的模板参数列表(在其类外的定义中)必须与类模板部分特化的模板参数列表匹配.这意味着,对于类模板的上述部分特化,您无法定义:

template <class A>
int Thing<A,double>::doSomething(); //error
Run Code Online (Sandbox Code Playgroud)

它是不允许的,因为函数定义中的模板参数列表与类模板部分特化的模板参数列表不匹配.标准(2003)的§14.5.4.3/ 1说,

类模板部分特化的成员的模板参数列表应匹配类模板部分特化的模板参数列表.[...]

有关详细信息,请在此处阅读我的回答:

C++ - 重载模板化类方法,并对该方法进行部分说明


那么解决方案是什么?您是否会将所有重复性工作部分专门化为您的课程?

一个简单的解决方案是工作委派,而不是部分专门化类模板.编写一个独立的函数模板,并将其专门化为:

template <class B>
B doTheActualSomething(B & b) { return b;  }

template <>
int doTheActualSomething<int>(int & b) { return b + 1; }
Run Code Online (Sandbox Code Playgroud)

然后从doSomething()成员函数调用此函数模板:

template <class A, class B>
B Thing<A,B>::doSomething() { return doTheActualSomething<B>(b_); }
Run Code Online (Sandbox Code Playgroud)

因为在你的特定情况下,doTheActualSomething需要知道只有一个成员的值,即b_上面的解决方案没问题,因为你可以将值作为参数传递给函数,其类型是模板类型参数B,并且int可以将其作为特征化全专业化.

但想象一下,如果它需要访问多个成员,类型各的依赖模板的类型参数列表,然后定义一个独立的函数模板不会解决问题,因为现在会有不止一个类型的函数参数模板,你不能部分专门化一个类型的功能(因为它是不允许的).

因此,在这种情况下,您可以定义一个类模板,它定义一个静态非模板成员函数doTheActualSomething.方法如下:

template<typename A, typename B>
struct Worker
{
   B doTheActualSomething(Thing<A,B> *thing)
   {
      return thing->b_;
   }
};

//partial specialization of the class template itself, for B = int
template<typename A>
struct Worker<A,int>
{
   int doTheActualSomething(Thing<A,int> *thing)
   {
      return thing->b_ + 1;
   }
};
Run Code Online (Sandbox Code Playgroud)

请注意,您可以使用thing指针访问该类的任何成员.当然,如果它需要访问私有成员,那么你要成为类模板struct Worker的朋友Thing,如:

//forward class template declaration
template<typename T, typename U> struct Worker

template <class A, class B>
class Thing
{
    template<typename T, typename U>  friend struct Worker; //make it friend
   //...
};
Run Code Online (Sandbox Code Playgroud)

现在将工作委托给朋友:

template <class A, class B>
B Thing<A,B>::doSomething()
{
    return Worker<A,B>::doTheActualSomething(this); //delegate work
}
Run Code Online (Sandbox Code Playgroud)

这里要注意两点:

  • 在此解决方案中,doTheActualSomething不是成员函数模板.它不是封闭的类,它是模板.因此,我们可以随时部分地专门化类模板,以获得部分成员函数模板特化的期望效果.
  • 因为我们将this指针作为参数传递给函数,所以我们可以访问类的任何成员Thing<A,B>,甚至是私有成员,因为Worker<T,U>它也是朋友.

完整的在线演示:http://www.ideone.com/uEQ4S


现在仍有改进的机会.现在,Worker类模板的所有实例都是Thing类模板的所有实例化的朋友.所以我们可以限制这种多对多的友谊:

template <class A, class B>
class Thing
{
    friend struct Worker<A,B>; //make it friend
   //...
};
Run Code Online (Sandbox Code Playgroud)

现在,只有一个Worker类模板的实例化是类模板的一个实例化的朋友Thing.这是一对一的友谊.也就是说,Worker<A,B>是的朋友Thing<A,B>.Worker<A,B>不是的朋友Thing<A,C>.

这种变化要求我们以不同的顺序编写代码.查看完整的演示,包括类和函数定义的所有顺序以及所有:

http://www.ideone.com/6a1Ih

  • 答案是错误的:*不允许专门化类模板的成员函数的专业化*不允许*是**false**.您不能*部分*在任何上下文中专门化成员函数,但您可以专门化类模板的成员函数.`template <typename T> struct test {void foo(){}}; template <> void test <int> :: foo(){std :: cout <<"specialized"; }`完全有效. (2认同)

Joh*_*itb 6

这是一个经常被发现的问题,并且有一个非常简单的解决方案.我将在一个人为的例子中展示它,因为它比使用你的代码更清晰,你必须理解它以使它适应你的代码

template<typename A, typename B>
struct TwoTypes { };

template<typename A, typename B>
struct X {
  /* forwards ... */
  void f() { fImpl(TwoTypes<A, B>()); }

  /* special overload for <A, int> */
  template<typename A1>
  void fImpl(TwoTypes<A1, int>) {
    /* ... */
  }

  /* generic */
  template<typename A1, typename B1>
  void fImpl(TwoTypes<A1, B1>) {
    /* ... */
  }
};
Run Code Online (Sandbox Code Playgroud)

明确专门化的功能绝不是(几乎从不?)正确的方式.在我作为程序员的工作中,我从未明确专门化一个函数模板.重载和部分订购是优越的.