模板成员函数的部分模板规范,用模板参数禁用类的实例化

时间:2018-01-31 20:27:05

标签: c++ templates

以下代码背后的想法是仅为具有特定大小的类型启用该类,如以下示例中的1:

#include <assert.h>

template<typename T>
class X
{
  private:

  template<int S=sizeof(T)>
  inline void foo(void) {static_assert(false,"illegal type");}

};

template<typename T> template<int S>
inline void X<T>::foo<1>(void){};

int main()
{

  X<char> x;
  return 0;
}

但是这不能在gcc中编译,它会因以下错误消息而失败:

q.cpp: In member function ‘void X<T>::foo()’:
q.cpp:9:26: error: static assertion failed: illegal type
   inline void foo(void) {static_assert(false,"illegal type");}
                          ^~~~~~~~~~~~~
q.cpp: At global scope:
q.cpp:14:30: error: non-class, non-variable partial specialization ‘foo<1>’ is not allowed
 inline void X<T>::foo<1>(void){};
                              ^
q.cpp:14:13: error: redefinition of ‘void X<T>::foo()’
 inline void X<T>::foo<1>(void){};
             ^~~~
q.cpp:9:15: note: ‘void X<T>::foo()’ previously declared here
   inline void foo(void) {static_assert(false,"illegal type");}

定义foo以获得所需结果的正确语法是什么?

2 个答案:

答案 0 :(得分:3)

您的版本失败,因为static_assert(false)始终为false,编译器可以在早期诊断它。这里的常见技巧是使条件依赖于模板参数并欺骗编译器认为它无法评估条件,但在您的情况下,您可以更容易地做到这一点:

template<typename T>
class X
{
    static_assert(sizeof(T) == 1, "illegal type");
};

现在您可以看到,条件显然取决于T,因此编译器只会在使用特定static_assert实例化X时诊断T,这是正是你想要的。

答案 1 :(得分:1)

您的代码存在一些问题。

  1. 即使模板从未实例化([temp.res] /8.1),也无法生成有效特化的模板会导致程序格式错误。这意味着static_assert(false, "illegal type")永远不能成为一个格式良好的计划的一部分;您必须使条件取决于模板参数。

  2. 无法为未经专门化的类模板([temp.expl.spec] / 16)的成员声明显式(完整)特化,这是您尝试使用的X<T>::foo

  3. 即使不是上述情况,如果您将static_assert条件更改为S == 1,也无法阻止封闭类模板的实例化,因为(a)实例化类模板不会自动实例化其成员函数的定义,并且(b)为所有模板参数提供默认参数并不会自动导致模板使用这些默认参数进行实例化。只有在您调用x.foo()或执行其他需要定义的其他内容时才会进行实例化。

  4. 最好将static_assert直接放在课堂上,而不是这首歌和舞蹈:

    template<typename T>
    class X {
        static_assert(sizeof(T) == 1, "illegal type");
        // ...
    };
    

    static_assert放在那里是合法的,因为static_assert是声明,而不是表达。