创建专门化函数模板的最佳方法是什么?
What is the best way to create a specialization-only function template?
是否有更好的方法来做以下事情?
#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
}
这个解决方案的问题是,它在(可以理解的)链接时间失败,"对浮点Bar<float>()
的未定义引用"或类似的。这可能会让其他开发人员感到困惑,因为他们可能会怀疑没有链接实现文件。
我知道另一个可能的解决方案:
template <typename T>
T Bar() { BOOST_STATIC_ASSERT(sizeof(T) == 0); }
当请求Bar<float>()
时,这会导致编译器错误,这正是我想要的。然而,我担心从技术上讲编译器可能会拒绝这一点,就像gcc拒绝BOOST_STATIC_ASSERT(false)
一样,因为它知道无论模板参数如何,它都会失败,因为sizeof(T)
可以永远为零。
总之,我想知道是否:
- 还有另一种方法。
- 我错了,没有实例化
BOOST_STATIC_ASSERT(sizeof(T))
实际上不会失败。 - 唯一的方法就是像上面那样让它是一个链接错误。
可以这样做:
template <typename T>
T Bar() {
T::ERROR_invalid_template_argument_;
}
template <>
int Bar<int>() { return 3; }
如果你害怕使用0:
,你也可以使用最大可能的大小。 static_assert(sizeof(T) == -1, "No specialization");
BOOST_STATIC_ASSERT(sizeof(T) == 0);
在模板实例化之前是不允许失败的,所以我只做那个。你是正确的,BOOST_STATIC_ASSERT(false);
每次触发。
这样做的原因与两阶段名称查找有关。这本质上是这样的:当模板被编译时,它会被编译两次。编译器第一次看到模板时,它编译所有 的内容,除了依赖于模板形参的表达式,第二次编译发生在模板形参已知后,完全编译实例化。
这就是为什么BOOST_STATIC_ASSERT(false);
总是失败的原因:这里没有任何东西是依赖的,断言被立即处理,就好像函数根本不是模板一样。(注意,MSVC不实现两阶段查找,因此在实例化时失败,这是不正确的。)相反,因为T
是依赖的(§14.6.2.1),BOOST_STATIC_ASSERT(sizeof(T) == 0);
也是依赖的,并且在模板实例化之前不允许被检查。(无论何时,它都会失败。)
如果编译器试图考虑周到并提前失败,它将是不符合的。你应该可以依靠这些东西。也就是说,如果恐惧占据了你的最佳状态,那么真的让它等待是微不足道的:
BOOST_STATIC_ASSERT(sizeof(typename T::please_use_specializations) == 0);
这既保证会失败,也不可能让编译器正确地"聪明地"提前失败。
您可以这样做:
template <typename T>
T Bar()
{ T::unspecialized_method_called; }
当然,这假定T没有具有给定名称的成员,因此您必须相应地选择"错误消息"(例如,通过违反命名约定)。
using static_assert with c++0x
template <typename T>
void bar(){
static_assert(false, " invalid argument type");
}
this将在编译时引发错误。
对于c++ 98/2003,我们可以试试
template <typename T>
void bar(){
char invalid_arg_[0];
}
数组至少包含一个元素。所以编译器会报错。但是错误信息可能无法显示发生了什么。
如果您使用没有-pedantic的gcc,则需要注意一点,在这种情况下,可能有sizeof(T) == 0
-当T
是零长度数组时。
#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;
}
在这种情况下,你可以使用下面的方法来解决这个问题:
BOOST_STATIC_ASSERT(sizeof(T) == sizeof(T) + 1);
最好封装这个技巧,这样可以提高可读性,因为它表达了你的意图:
#define NEVER_INSTANTIATE(T) BOOST_STATIC_ASSERT(sizeof(T) == sizeof(T) + 1);
正如GMan解释的那样,没有实例化就不会失败,就像sizeof(T) == 0
一样。然而,这个故事的寓意应该是:总是用-pedantic来编译。
- 为不同配置设置MSVC_RUNTIME_LIBRARY的正确方法是什么
- 在C++中,将大的无符号浮点数四舍五入为整数的最佳方法是什么
- 实现无开销push_back的最佳方法是什么
- C++从另一个类访问公共静态向量的正确方法是什么
- 在 c++ 中拥有一组结构的正确方法是什么?
- 通过JNI传递数据数组的最快方法是什么
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 使用不同的CRT将新的C++代码与旧的(二进制)组件隔离开来的最佳方法是什么
- 当无法使用模板和宏时,生成类型变体C++代码的最简单方法是什么?
- 在另一个类视图中添加最多2个图表的正确方法是什么
- 在C++中样板"冷/never_inline"错误处理技术的最佳方法是什么?
- 在 c++ 中对类中的 c 字符串动态数组进行排序的最佳方法是什么?
- 在C++中包含原型文件的正确方法是什么?
- 在 OpenCV C++ 中估计基本矩阵之前对相应点进行归一化的正确方法是什么?
- 在PostgreSQL中根据它们的ID选择大量行的最快方法是什么?
- 在OSX上使用CMake将Adobe的XMP工具包构建为共享库的最简单方法是什么?
- 将一系列整数放入类的最佳方法是什么?
- 从长整整转换为uint64_t的推荐方法是什么?
- 将此布尔值传递给此函数的最有效方法是什么?
- 通过比较C++中的行在 txt 文件中搜索的最简单方法是什么?