没有默认功能的C ++模板专业化

C++ template specialization without default function

我有以下代码可以很好地编译和工作:

1
2
3
4
5
6
7
8
template<typename T>
T GetGlobal(const char *name);

template<>
int GetGlobal<int>(const char *name);

template<>
double GetGlobal<double>(const char *name);

不过,我想删除"默认"功能。也就是说,我想对getglobal进行所有调用,其中"t"不是int,也不是double。

例如,getglobal()应该是编译时错误。

我试图删除默认函数,但正如我想象的那样,我收到了很多错误。那么有没有一种方法可以"禁用"它并只允许调用函数的专门版本?

谢谢!


要获取编译时错误,请将其实现为:

1
2
3
template<typename T>
T GetGlobal(const char *name) { T::unimplemented_function; }
// `unimplemented_function` identifier should be undefined

如果使用Boost,可以使其更优雅:

1
2
template<typename T>
T GetGlobal(const char *name) { BOOST_STATIC_ASSERT(sizeof(T) == 0); }

C++标准保证不存在大小等于0的这样的类型,因此将获得编译时错误。

正如SBI在他的评论中建议的,最后一个可以简化为:

1
2
template<typename T>
T GetGlobal(const char *name) { char X[!sizeof(T)]; }

我更喜欢第一个解决方案,因为它给出的错误信息(至少在VisualC++中)比其他的更清楚。


虽然这是一个陈旧过时的问题,但值得注意的是,C++11使用删除的函数解决了这个问题:

1
2
3
4
5
template<typename T>
T GetGlobal(const char *name) = delete;

template<>
int GetGlobal<int>(const char *name);

更新

这不会在MacOS llvm 8下编译。这是由于一个仍然挂着4年的缺陷(见这个错误报告)。

以下解决方法将适合这个问题(使用static_assert构造)。

1
2
3
4
5
6
7
template<typename T>
T GetGlobal(const char *name) {
    static_assert(sizeof(T) == 0,"Only specializations of GetGlobal can be used");
}

template<>
int GetGlobal<int>(const char *name);

更新

Visual Studio 15.9有相同的错误。使用以前的解决方法。


如果不实现它,至少会得到一个链接器错误。如果需要编译时错误,可以使用类模板执行此操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
template<typename T>
struct GlobalGetter;

template<>
struct GlobalGetter<int> {
  static int GetGlobal(const char *name);
};

template<>
struct GlobalGetter<double> {
  static double GetGlobal(const char *name);
};

template<typename T>
T GetGlobal(const char *name)
{
  return GlobalGetter<T>::GetGlobal(name);
}


我建议不要实际提供一个实现,只提供方法的一个简单声明。

另一种选择是使用编译时断言。Boost有很多这样的野兽。

1
2
3
namespace mpl = boost::mpl;
BOOST_MPL_ASSERT((mpl::or_< boost::same_type<T, double>,
                            boost::same_type<T, int> >));

还有它的消息版本对应物,这将有所帮助。


以下是使用Boost的替代技术:

将typedef声明为依赖名称

这是因为名称查找不仅在"t"被替换时发生。这是kirill给出的示例的类似(但合法)版本

1
2
3
4
template <typename T>
T GetGlobal (const char * name) {
    typedef typename T::DONT CALL_THIS_FUNCTION;
}

使用不完整的返回类型

这项技术不适用于专门化,但适用于重载。其思想是,声明返回不完整类型但不调用该类型的函数是合法的:

1
2
template <typename T>
class DONT_CALL_THIS_FUNCTION GetGlobal (const char * name);