有时,在使用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;
};
有很多方法可以做到这一点(取决于你的专业化是一个类或函数的完全或部分特化)。但是使用的语法必须(?)依赖于模板参数,否则编译器会在首次解析故意错误定义时抱怨。上面的例子有一个漏洞,有人可能会顽固地定义一个intentional_error
嵌套类型或成员typedef(虽然我会说他们会因此而得到任何问题)。但是如果你使用的技巧过于花哨,你很可能会得到一个难以理解和/或误导性的编译器错误消息,这大部分都会失败。
是否有更好的直接方法来禁止模板实例化?
我知道在C ++ 0x中,模板Concepts和删除的函数声明将更好地控制这类事情,但我正在寻找有效C ++ 03的答案。
答案 0 :(得分:27)
你可以省略定义它。
template <typename T> struct MyClassTemplate<T*>;
您也可以从未定义的专业化派生
template <typename T> struct invalid;
template <typename T> struct MyClassTemplate<T*> : invalid<T> { };
请注意,声明类或函数的显式特化将永远不会依赖于模板参数。因此,依赖于模板参数的这样的东西无论如何都无法工作。在这种情况下,声明一个未定义的显式特化应该就足够了
template<> struct MyClassTemplate<int*>;
答案 1 :(得分:14)
对我来说,这听起来像来自C ++ 0x或static_assert的BOOST_STATIC_ASSERT的典型案例。 static_assert功能的优势在于您可以传递自定义错误消息,以便更明确错误原因。
这两种方式都使您有机会在某些自定义编译时条件下过早地结束编译过程。
with static_assert:
template <typename T> struct MyClassTemplate<T*> {
static_assert(always_false<T>::value, "Do not use MyClassTemplate with a pointer type!");
};
使用BOOST_STATIC_ASSERT
template <typename T> struct MyClassTemplate<T*> {
// Do not use MyClassTemplate with a pointer type!
BOOST_STATIC_ASSERT(always_false<T>::value);
};
总是假的看起来像这样:
template< typename T >
struct always_false {
enum { value = false };
};
HTH
修改:修复了让它们真正起作用的例子;-)感谢GMan!
答案 2 :(得分:2)
如果您不想使用库,这个构造非常可靠(它大致与Boost在内部的作用相同):
template <typename T>
void must_be_specialized(T const&)
{
enum dummy { d = (sizeof(struct must_be_specialized_for_this_type)
== sizeof(T)) };
}
您可以在专门化中添加类似的内容,以禁止使用该类型实例化模板。我个人不担心must_be_specialized_for_this_type
从某个地方获得定义,但如果你真的想要,你可以使用前向声明将它放在私有命名空间中。
答案 3 :(得分:1)
概念已从'0x中删除。您可以使用库,例如Boost Concept Check。
答案 4 :(得分:1)
“是否有更好的直接方法来禁止模板实例化?”没有什么比你已经确定的更好。我很确定C ++保护机制可以保护您免受意外事故的侵害。有人定义专业化或类来打破你的预期用途我会认为是恶意的。也许每次他们这样做时你都可以击中脑后的人。
我个人更喜欢将支票放入仅用于描述支票的模板中。这允许继承和模板的有趣组合。
template <class T>
class not_with_pointer_t { };
template <class T>
class not_with_pointer_t<T*>;
template <class T>
class some_class_t : public not_with_pointer_t<T> { };
template <class T, template <class U> class base_t>
class another_class_t : public base_t<T> { };
typedef some_class_t<int> a_t; // ok
typedef some_class_t<void*> b_t; // error if instantiated
typedef another_class_t<void*, not_with_pointer_t> c_t; // error if instantiated
template <class T> class unrestricted_t { };
typedef another_class_t<void*, unrestricted_t> d_t; // ok
答案 5 :(得分:0)
boost::enable_if