避免多个不依赖于模板类型的函数的模板瞬时的优雅方法
Elegant way to avoid multiple template instantiatons of function that doesn't depend on templated type
我有一个基本的类,看起来像这样:
template <typename TCode, TCode SuccessVal>
class Error
{
public:
typedef TCode code_t;
Error(TCode code, char const *descr="Unknown Error."):
code(code), descr(descr)
{
}
...
char const *description() const
{
return descr;
}
...
private:
TCode code;
char const *descr;
};
它所做的只是封装某种错误代码枚举类,以便为日志记录提供更多的上下文。
现在假设我有一个函数panic
:
template <typename TCode, TCode SuccessVal>
void panic(Error<TCode, SuccessVal> const &e)
{
puts("Panic!");
printf("Unrecoverable error: %s", e.description());
abort();
}
使用-fdump-tree-original编译显示,在我的例子中,这将产生两个完全相同代码的不同函数。这是您所期望的,但可能不是您所希望的。
一个明显的路径是一个基类,它只有消息和一个接受消息的构造函数,但我发现这相当不吸引人。
我们从不使用错误代码本身,所以我们所做的一切都不依赖于t。我如何避免大量的模板实例化编译成基本相同的代码?
另一个理想的特性是确保无论TCode是什么类型,它都可以强制转换为整数类型。
这段代码的一个明显的分解是:
[[noreturn]] void panic_with_message(char const * msg)
{
std::printf("Panic!nUnrecoverable error: %sn", msg);
std::fflush(stdout);
std::abort();
}
template <typename T, T Val>
[[noreturn]] static inline void panic(Error<T, Val> e)
{
panic_with_message(e.description());
}
可以只将模板和函数声明放在头文件中,并将函数定义放在单独的翻译单元中。这将使代码膨胀降到最低:
// panic.hpp
#ifndef H_PANIC
#define H_PANIC
#include "error.hpp" // for Error<T, Val>
[[noreturn]] void panic_with_message(char const * msg);
template <typename T, T Val>
[[noreturn]] static inline void panic(Error<T, Val> e)
{
panic_with_message(e.description());
}
#endif
另一个选择是使用外部模板声明。c++11编译器和更早的visual studio版本支持此功能(作为语言扩展)。假设您有相当少的Error实例化,您可以为每个实例化定义一个外部的panic模板声明,并将实际的函数实例化放在单个翻译单元中。这可以确保每个panic变体只有一个实例化。
panic.hpp
#ifndef PANIC_HPP
#define PANIC_HPP
template <typename TCode, TCode SuccessVal>
struct Error
{
const char* description() { return "boom!"; }
};
template <typename TCode, TCode SuccessVal>
void panic(Error<TCode, SuccessVal> e);
//! So assume you have Error<ErrorCode, SuccessVal>
//! Place this in the header where the panic function prototype is declared.
//! You'll need one for each type or Error<T,Val> instantiation.
extern template void panic<int, 0>(Error<int, 0> e);
extern template void panic<int, 1>(Error<int, 1> e);
extern template void panic<int, 2>(Error<int, 2> e);
#endif//PANIC_HPP
panic.ipp
#ifndef PANIC_IPP
#define PANIC_IPP
#include "panic.hpp"
#include <cstdio>
#include <cstdlib>
template <typename TCode, TCode SuccessVal>
inline void panic(Error<TCode, SuccessVal> e)
{
printf("Unrecoverable error: %s scode: %dn", e.description(), SuccessVal);
std::abort();
}
#endif//PANIC_IPP
panic.cpp
#include "panic.ipp"
//! Declare the instantiation for each Error<T, Val> which you don't want duplicated.
template void panic<int, 0>(Error<int, 0> e);
template void panic<int, 1>(Error<int, 1> e);
template void panic<int, 2>(Error<int, 2> e);
main.cpp
#include "panic.hpp"
int main()
{
panic(Error<int, 0>());
panic(Error<int, 1>());//! These compile/link but aren't called due to abort.
panic(Error<int, 2>());//! ...
//panic(Error<int, 3>()); //! won't link due to unresolved symbol as not instantiated.
return 0;
}
相关文章:
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 使用std::函数映射对象方法
- 有没有什么方法可以使用一个函数中定义的常量变量,也可以由c++中同一程序中的其他函数使用
- 初始化具有非默认构造函数的std::数组项的更好方法
- 如何制作一个将函数作为参数的类方法
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 获取从C++中同一类中的构造函数调用的方法返回的值
- C++ - 类方法函数指针unordered_map的初始化器列表
- 如何从具有多个对象/字段的类中调用方法函数
- 如何以这种方式访问另一个文件的方法/函数
- Vim 转到标准库方法/函数定义
- 在方法/函数中使用"static const std::string"还是只使用"cons
- 我可以将这两种方法/函数合二为一吗?
- 为什么我不能添加到方法/函数外部的 ostringstream
- 如何在调用方法函数中将列表、向量或数组作为参数传递
- 如何专门化模板化类,以采用不带参数的方法函数类型
- 类方法/函数中的指针
- 修改参数和/或返回新实例的方法/函数的命名约定
- 在Lua中跟踪变量以进行读访问,以启动用户定义的c++方法/函数
- 调试可视化工具在预览中使用成员方法/函数调用