如何在模板实例化时故意造成编译时错误

asc*_*ler 25 c++ templates design-patterns

有时,在使用C++模板进行编码时,您希望阻止用户实例化特定的特化或一组特化,因为结果将是荒谬的.因此,您可以定义(特定或部分)特化,如果实例化,其定义将导致编译器错误.如果用户"误用"模板,目标将是在头文件中的注释旁边引发编译器错误,解释不应该做什么,而不是让编译器自己提出一些令人困惑的错误消息设备,或者可能允许编译可疑代码.

例:

template <typename T> struct MyClassTemplate {
  // ...
};

template <typename T> struct MyClassTemplate<T*> {
  // Do not use MyClassTemplate with a pointer type!
  typedef typename T::intentional_error err;
};
Run Code Online (Sandbox Code Playgroud)

有很多方法可以做到这一点(取决于你的专业化是一个类或函数的完全或部分特化).但是使用的语法必须(?)依赖于模板参数,否则编译器会在首次解析故意错误定义时抱怨.上面的例子有一个漏洞,有人可能会顽固地定义一个intentional_error嵌套类型或成员typedef(虽然我会说他们会因此而得到任何问题).但是,如果你使用的技巧过于花哨,你很可能会得到一个难以理解和/或误导性的编译器错误消息,这大部分都会失败.

是否有更好的直接方法来禁止模板实例化?

我知道在C++ 0x中,模板Concepts和删除的函数声明将更好地控制这类事情,但我正在寻找有效的C++ 03答案.

Joh*_*itb 27

你可以省略定义它.

template <typename T> struct MyClassTemplate<T*>;
Run Code Online (Sandbox Code Playgroud)

您还可以从未定义的专业化派生出来

template <typename T> struct invalid;
template <typename T> struct MyClassTemplate<T*> : invalid<T> { };
Run Code Online (Sandbox Code Playgroud)

请注意,声明类或函数的显式特化将永远不会依赖于模板参数.所以,依赖于模板参数的这样的东西无论如何都无法工作.在这种情况下,声明一个未定义的显式特化应该就足够了

template<> struct MyClassTemplate<int*>;
Run Code Online (Sandbox Code Playgroud)

  • @aschepler:俗话说,在C++中,你可以保护自己免受墨菲(有时丑陋的伎俩),但你无法保护自己免受马基雅维利的伤害. (5认同)

Vin*_*enz 14

对我来说,这听起来像是来自C++ 0x或BOOST_STATIC_ASSERT的static_assert的典型案例.该static_assert功能的优点是,你可以通过自定义错误消息,这样的错误的原因是更清晰.

这两种方式都使您有机会在某些自定义编译时条件下过早地结束编译过程.

使用static_assert:

template <typename T> struct MyClassTemplate<T*> {
    static_assert(always_false<T>::value, "Do not use MyClassTemplate with a pointer type!");
};
Run Code Online (Sandbox Code Playgroud)

使用BOOST_STATIC_ASSERT

template <typename T> struct MyClassTemplate<T*> {
    // Do not use MyClassTemplate with a pointer type!
    BOOST_STATIC_ASSERT(always_false<T>::value);
};
Run Code Online (Sandbox Code Playgroud)

总是假的看起来像这样:

template< typename T >
struct always_false { 
    enum { value = false };  
};
Run Code Online (Sandbox Code Playgroud)

HTH

编辑:修复了使它们实际工作的例子;-)感谢GMan!

  • 这些将一直无条件地断言.(也就是说,编译器会看到`false`,知道这个静态断言失败,所以触发诊断.)如果你想要它只在实例化时触发它,你需要使它依赖于模板参数,如下所示:`static_assert( sizeof(T)== 0,"blah");`. (6认同)
  • 所以我修复了它实际工作的例子:-) (2认同)