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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6138439/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 19:32:51  来源:igfitidea点击:

Understanding (simple?) C++ Partial Template Specialization

c++templatestemplate-specialization

提问by Dan

Note:this seems to be a repost of a problem: C++ - Overload templated class method with a partial specilization of that method

注意:这似乎是一个问题的重新发布:C++ - Overload templated class method with a partial specilization of that method

I have boiled down a problem I am having with C++ template specialization down to a simple case.

我已经将我在 C++ 模板专业化方面遇到的问题归结为一个简单的案例。

It consists of a simple 2-parameter template class Thing, where I would like to specialize Thing<A,B>::doSomething()for B=int.

它由一个简单的 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() {
    // 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;
}

Unfortunately, g++exits with the error:

不幸的是,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>'

The clang++compiler is a bit more verbose, but has the same problem:

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.

I have read and understood that partial template specializations on functions aren't allowed - but I thought I was partially specializing over classes of Thingin this case.

我已经阅读并理解不允许对函数进行部分模板专业化 - 但我认为我Thing在这种情况下部分专业化了 of 的类。

Any ideas?

有任何想法吗?

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

回答by Nawaz

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 matchthe 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

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,

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

The template parameter list of a member of a class template partial specialization shall matchthe template parameter list of the class template partial specialization.[...]

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

For more on this, read my answer here:

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

C++ - Overload templated class method with a partial specilization of that method

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-alonefunction template and specialize this as:

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

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

template <>
int doTheActualSomething<int>(int & b) { return b + 1; }

And then call this function template from doSomething()member function as:

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

template <class A, class B>
B Thing<A,B>::doSomething() { return doTheActualSomething<B>(b_); }


Since in your particular case, doTheActualSomethingneeds to know the value of only onemember, namely b_, the above solution is fine, as you can pass the value to the function as argument whose type is the template typeargument B, and specialization for intis possible being it full-specialization.

由于在您的特定情况下,只doTheActualSomething需要知道一个成员的值,即b_,上述解决方案很好,因为您可以将该值作为参数传递给函数,其类型为模板类型参数B,并且int可以对其进行专门化全专业。

But imagine if it needs to access multiple members, typeof each depends on the template typeargument-list, then defining a stand-alone function template wouldn't solve the problem, because now there will be more than one typeargument to the function template, and you cannot partiallyspecialize the function for just, say, one type(as its not allowed).

但是想象一下,如果它需要访问多个成员,每个成员的类型取决于模板类型参数列表,那么定义一个独立的函数模板并不能解决问题,因为现在函数的类型参数将不止一个模板,并且您不能将函数部分地专门化为一种类型(因为它不允许)。

So in this case you can define a class template instead, which defines a static non-template member function doTheActualSomething. Here is how:

所以在这种情况下,你可以定义一个类模板,它定义了一个静态的非模板成员函数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;
   }
};

Notice that you can use thingpointer to access any member of the class. Of course, if it needs to access private members, then you've to make struct Workera friend of Thingclass template, as:

请注意,您可以使用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
   //...
};

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
}

Two points to be noted here:

这里需要注意两点:

  • In this solution, doTheActualSomethingis not a member function template. Its not enclosing class which is template. Hence we can partiallyspecialize the class template anytime, to get the desired effect of the partialmember function template specialization.
  • Since we pass thispointer as argument to the function, we can access any member of the class Thing<A,B>, even private members, as Worker<T,U>is also a friend.
  • 在这个解决方案中,doTheActualSomething不是成员函数模板。它不是包含模板的类。因此,我们可以随时对类模板进行部分特化,以获得部分成员函数模板特化的预期效果。
  • 由于我们将this指针作为参数传递给函数,因此我们可以访问类的任何成员Thing<A,B>,甚至是私有成员,就像Worker<T,U>朋友一样。

Complete online demo : http://www.ideone.com/uEQ4S

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



Now there is still a chance of improvement. Now all instantiations of Workerclass template are friends of all instantiation of Thingclass template. So we can restrict this many-to-many friendship as:

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

template <class A, class B>
class Thing
{
    friend struct Worker<A,B>; //make it friend
   //...
};

Now only one instantiation of Workerclass template is a friend of one instantiation of Thingclass 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>.

现在只有一个Worker类模板实例化是一个类模板实例化的朋友Thing。那是一对一的友谊。也就是说,Worker<A,B>是 的朋友Thing<A,B>Worker<A,B>不是 的朋友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

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

回答by Johannes Schaub - litb

This is a very often found problem, and there is a surprisingly simplesolution. I will show it in an artificial example, because it's more clearer than to use your code, and you will have to understand it to adapt it to your code

这是一个非常常见的问题,并且有一个非常简单的解决方案。我将在一个人工示例中展示它,因为它比使用您的代码更清晰,您必须理解它才能使其适应您的代码

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>) {
    /* ... */
  }
};

Explicitly specializing functions is never (almost never?) the right way. In my work as a programmer, I've never explicitly specialized a function template. Overloading and partial ordering is superior.

明确地专门化函数从来不是(几乎从来没有?)正确的方法。在我作为程序员的工作中,我从未明确指定函数模板。重载和偏序更胜一筹。