c++ - 理解(简单?)C++ 部分模板特化

标签 c++ templates template-specialization

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

我将 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() {
    // 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;
}

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

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.

我已阅读并理解不允许对函数进行部分模板特化 - 但我认为我部分特化了 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_);
}

最佳答案

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

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

但您可以部分特化类模板本身。你可以这样做:

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 */ }

注意,当你对一个类模板进行部分特化时,那么成员函数的模板参数列表(在其类外的定义中),必须匹配类模板部分特化的模板参数列表.这意味着,对于类模板的上述部分特化,您不能这样定义:

template <class A>
int Thing<A,double>::doSomething(); //error

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

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.[...]

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

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


那么解决方法是什么?您会在所有重复性工作的同时部分特化您的类(class)吗?

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

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

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

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

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

因为在您的特殊情况下,doTheActualSomething需要知道只有一个成员的值,即b_ ,上面的解决方案很好,因为您可以将值作为参数传递给函数,其类型是模板 type 参数 B , 和特化 int有可能是全特化。

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

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

请注意,您可以使用 thing访问类的任何成员的指针。当然,如果它需要访问私有(private)成员,那么你必须使struct Worker Thing 的 friend 类模板,如:

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

现在将工作委托(delegate)给 friend :

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

这里需要注意两点:

  • 在此解决方案中,doTheActualSomething不是成员函数模板。它不包含模板类。因此,我们可以随时partially特化类模板,以获得partial成员函数模板特化的预期效果。
  • 自从我们通过 this指针作为函数的参数,我们可以访问类的任何成员 Thing<A,B> ,甚至私有(private)成员,如 Worker<T,U>也是 friend 。

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


现在仍有改进的机会。现在 Worker 的所有实例化类模板是 Thing 的所有实例化的 friend 类模板。所以我们可以将这种多对多的友元限制为:

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

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

此更改要求我们以稍微不同的顺序编写代码。查看完整的演示,包含类和函数定义的所有顺序以及所有内容:

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

关于c++ - 理解(简单?)C++ 部分模板特化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6138439/

相关文章:

c++ - 复制初始化和直接初始化有区别吗?

c++ - 从模板化模板类和可变模板中声明 "container"对象

c++ - 使用一些 C++ 模板包装器代码将自己陷入 const 角落

c++ - 模板类型的奇怪行为

具有 const 限定符的自由函数类型的 C++ 特化

c++ - 是否实例化了不必要/未使用的模板?

c++ - 从 Poco HTTPClientSession 异步读取

c++ - 如何使用 libpd 从纯数据补丁接收命名信号?

c++ - C++ 中的全局变量是存储在堆栈、堆还是两者都不存储?

json - 在同一Logstash配置文件中使用多个Elsticsearch输出时,将忽略模板