理解(简单?)C++ 部分模板专业化
注意:这似乎是一个问题的转贴:C++ - 重载模板化的类方法,部分特化该方法
Note: this seems to be a repost of a problem: C++ - Overload templated class method with a partial specilization of that method
我已经将我在 C++ 模板专业化中遇到的问题归结为一个简单的案例.
I have boiled down a problem I am having with C++ template specialization down to a simple case.
它由一个简单的 2 参数模板类 Thing
组成,我想在其中专门化 Thing<A,B>::doSomething()
用于 B=int
.
It consists of a simple 2-parameter template class Thing
, where I would like to specialize Thing<A,B>::doSomething()
for 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() {
// 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
", thing.doSomething());
// Clean up.
return 0;
}
不幸的是,g++
退出并出现错误:
Unfortunately, g++
exits with the error:
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>’
clang++
编译器有点冗长,但也有同样的问题:
The clang++
compiler is a bit more verbose, but has the same problem:
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.
我已阅读并了解不允许对函数进行部分模板特化 - 但我认为在这种情况下我部分特化了 Thing
的类.
I have read and understood that partial template specializations on functions aren't allowed - but I thought I was partially specializing over classes of Thing
in this case.
有什么想法吗?
我做了什么:一种解决方法,根据接受的答案提供的链接确定:
What I did: A workaround, as determined from the link provided by the accepted answer:
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_);
}
推荐答案
函数模板的部分特化,无论是成员函数模板还是独立函数模板,都是标准不允许的:
Partial specialization of a function template, whether it is member function template or stand-alone function template, is not allowed by the Standard:
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
但是您可以部分特化类模板本身.你可以这样做:
But you can partially specialize the class template itself. You can do something like this:
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 */ }
注意,当你部分特化一个类模板时,成员函数的模板参数列表(在类外的定义中),必须匹配类模板部分特化的模板参数列表.这意味着,对于类模板的上述部分特化,你不能定义这个:
Note that when you partially specialize a class template, then the template parameter-list of member function (in its definition outside the class), must match the template parameter list of the class template partial specialization. That means, for the above partial specialization of the class template, you cannot define this:
template <class A>
int Thing<A,double>::doSomething(); //error
这是不允许的,因为函数定义中的模板参数列表与类模板部分特化的模板参数列表不匹配.标准 (2003) 中的 §14.5.4.3/1 说,
Its not allowed, because the template parameter-list in function definition didn't match the template parameter-list of the class template partial specialization. §14.5.4.3/1 from the Standard (2003) says,
类模板部分特化成员的模板参数列表应匹配类模板部分特化的模板参数列表.[...]
The template parameter list of a member of a class template partial specialization shall match the template parameter list of the class template partial specialization.[...]
有关这方面的更多信息,请在此处阅读我的回答:
For more on this, read my answer here:
C++- 重载模板化类方法,使用该方法的部分专业化
那么解决方案是什么?除了所有重复性工作外,您会部分专业化您的课程吗?
So what is the solution? Would you partially specialize your class along with all the repetitive work?
一个简单的解决方案是工作委托,而不是部分专门化类模板.编写一个独立函数模板并将其特化为:
A simple solution would be work delegation, instead of partially specializing the class template. Write a stand-alone function template and specialize this as:
template <class B>
B doTheActualSomething(B & b) { return b; }
template <>
int doTheActualSomething<int>(int & b) { return b + 1; }
然后从 doSomething()
成员函数调用这个函数模板:
And then call this function template from doSomething()
member function as:
template <class A, class B>
B Thing<A,B>::doSomething() { return doTheActualSomething<B>(b_); }
<小时>
由于在您的特定情况下,doTheActualSomething
需要知道只有一个 成员的值,即 b_
,上述解决方案就可以了,因为您可以将值作为参数传递给函数,其类型是模板 type 参数 B
,并且 int
的特化可能是完整的-专业化.
Since in your particular case, doTheActualSomething
needs to know the value of only one member, namely b_
, the above solution is fine, as you can pass the value to the function as argument whose type is the template type argument B
, and specialization for int
is possible being it full-specialization.
但是想象一下,如果需要访问多个成员,每个成员的type取决于模板type参数列表,那么定义一个独立的函数模板就不会了解决问题,因为现在函数模板将有多个 type 参数,并且您不能部分将函数专门化为一个类型(因为它不允许).
But imagine if it needs to access multiple members, type of each depends on the template type argument-list, then defining a stand-alone function template wouldn't solve the problem, because now there will be more than one type argument to the function template, and you cannot partially specialize the function for just, say, one type (as its not allowed).
所以在这种情况下你可以定义一个类模板,它定义一个静态非模板成员函数doTheActualSomething
.方法如下:
So in this case you can define a class template instead, which defines a static non-template member function doTheActualSomething
. Here is how:
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;
}
};
请注意,您可以使用 thing
指针来访问类的任何成员.当然,如果它需要访问私有成员,那么你必须让 struct Worker
成为 Thing
类模板的朋友,如:
Notice that you can use thing
pointer to access any member of the class. Of course, if it needs to access private members, then you've to make struct Worker
a friend of Thing
class template, as:
//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
//...
};
现在将工作委托给朋友:
Now delegate the work to the friend as:
template <class A, class B>
B Thing<A,B>::doSomething()
{
return Worker<A,B>::doTheActualSomething(this); //delegate work
}
这里要注意两点:
- 在这个解决方案中,
doTheActualSomething
不是成员函数模板.它不是包含模板的类.因此,我们可以随时部分特化类模板,以获得部分成员函数模板特化的预期效果. - 由于我们将
this
指针作为参数传递给函数,我们可以访问Thing
类的任何成员,甚至私有成员,作为Worker
也是朋友.
- In this solution,
doTheActualSomething
is not a member function template. Its not enclosing class which is template. Hence we can partially specialize the class template anytime, to get the desired effect of the partial member function template specialization. - Since we pass
this
pointer as argument to the function, we can access any member of the classThing<A,B>
, even private members, asWorker<T,U>
is also a friend.
完整的在线演示:http://www.ideone.com/uEQ4S
现在仍有改进的机会.现在Worker
类模板的所有实例化都是Thing
类模板的所有实例化的朋友.所以我们可以将这种多对多的友谊限制为:
Now there is still a chance of improvement. Now all instantiations of Worker
class template are friends of all instantiation of Thing
class template. So we can restrict this many-to-many friendship as:
template <class A, class B>
class Thing
{
friend struct Worker<A,B>; //make it friend
//...
};
现在只有一个 Worker
类模板的实例是 Thing
类模板的一个实例的朋友.那是一对一的友谊.也就是说,Worker
是 Thing
的朋友.Worker
不是 Thing
的朋友.
Now only one instantiation of Worker
class template is a friend of one instantiation of Thing
class template. That is one-to-one friendship. That is, Worker<A,B>
is a friend of Thing<A,B>
. Worker<A,B>
is NOT a friend of Thing<A,C>
.
此更改要求我们以稍微不同的顺序编写代码.查看完整的演示,以及所有类和函数定义的顺序:
This change requires us to write the code in somewhat different order. See the complete demo, with all the ordering of class and function definitions and all:
http://www.ideone.com/6a1Ih
相关文章