What is the best way to create a specialization-only function template?
是否有更好的方法来执行以下操作?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #include <iostream> template <typename T> T Bar(); template <> int Bar<int>() { return 3; } // Potentially other specialisations int main() { std::cout << Bar<int>() << std::endl; // This should work std::cout << Bar<float>() << std::endl; // This should fail } |
这个解决方案的问题是,它在(可以理解)链接时间失败,出现了"对float
我知道另一个可能的解决方案:
1 2 | template <typename T> T Bar() { BOOST_STATIC_ASSERT(sizeof(T) == 0); } |
这会在请求
总之,我想知道:
这可能有效:
1 2 3 4 5 6 7 | template <typename T> T Bar() { T::ERROR_invalid_template_argument_; } template <> int Bar<int>() { return 3; } |
如果您害怕使用0,也可以使用最大尺寸:
1 | static_assert(sizeof(T) == -1,"No specialization"); |
在模板被实例化之前,不允许
这与两阶段名称查找有关。这基本上是以下内容:当一个模板被编译时,它被编译了两次。第一次编译器看到模板时,它会编译除依赖于模板参数的表达式之外的所有内容,第二次编译在模板参数已知时进行,完全编译实例化。
这就是为什么
如果一个编译器试图深思熟虑并提前失败,那么它将是不一致的。你应该可以依靠这些东西。也就是说,如果恐惧让你变得最好,那么真正让它等待是微不足道的:
1 | BOOST_STATIC_ASSERT(sizeof(typename T::please_use_specializations) == 0); |
这既保证了失败,也不可能让编译器提前正确地"聪明地"失败。
有一点需要注意的是,如果你使用GCC时不带学问,在这种情况下,当
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include <iostream> #include"boost/static_assert.hpp" template <typename T> void Foo() { BOOST_STATIC_ASSERT(sizeof(T) == 0); std::cout <<"Actually, it is possible to instantiate this." << std::endl; } int main() { Foo<int[0]>(); return 0; } |
在这种情况下,您可以使用它来解决问题:
1 | BOOST_STATIC_ASSERT(sizeof(T) == sizeof(T) + 1); |
最好将此技巧封装起来,这样可以提高可读性,因为它表达了您的意图:
1 | #define NEVER_INSTANTIATE(T) BOOST_STATIC_ASSERT(sizeof(T) == sizeof(T) + 1); |
正如gman所解释的那样,如果没有像
使用C++0X使用STATICE-ASPART
1 2 3 4 | template <typename T> void bar(){ static_assert(false," invalid argument type"); } |
这将在编译时引发错误。
对于C++ 98/2003,我们可以试试这个
1 2 3 4 | template <typename T> void bar(){ char invalid_arg_[0]; } |
数组至少包含一个元素。所以编译器会抱怨。但是错误消息可能无法显示发生了什么。
第一个不会是一个选择,因为它总是失败的。
您可以执行以下操作:
1 2 3 | template <typename T> T Bar() { T::unspecialized_method_called; } |
当然,这假定没有具有给定名称的成员,因此您必须相应地选择"错误消息"(例如,违反命名约定)。