C++11中为何无法将对象引用/指针作为模板实参?
我在C++11环境下尝试将对象的引用/指针作为模板参数使用,编写了以下代码:
引用版本代码
#include <iostream> #include <vector> struct LogParamScaler { }; template <LogParamScaler &T> struct QRange { }; int main() { LogParamScaler mLogParamScaler; QRange<mLogParamScaler> qRange; }
编译器报错:
'& mLogParamScaler' is not a valid template argument of type 'LogParamScaler&' because 'mLogParamScaler' has no linkage
指针版本代码
改用指针尝试后依然失败:
#include <iostream> #include <vector> struct LogParamScaler { }; template <LogParamScaler *T> struct QRange { }; int main() { LogParamScaler mLogParamScaler; QRange<&mLogParamScaler> qRange; }
我的核心需求是必须使用模板,因为QRange<mLogParamScaler>需要作为类型传入其他模板(例如createObject<QRange<mLogParamScaler>>),请问该如何实现?
在C++11中,模板的非类型参数如果是引用或指针,要求对应的变量必须具备链接性(linkage)——局部变量(比如main函数内的mLogParamScaler)没有链接性,编译器无法在编译期确定其地址,因此不能作为模板参数。以下是几种可行的实现方式:
方法1:使用全局/静态变量
将目标对象声明为全局变量或静态局部变量,使其获得链接性:
全局变量版本
#include <iostream> #include <vector> struct LogParamScaler { }; // 全局变量具备外部链接性 LogParamScaler mLogParamScaler; template <LogParamScaler &T> struct QRange { }; int main() { QRange<mLogParamScaler> qRange; // 可正常传入其他模板 // createObject<QRange<mLogParamScaler>>(); }
静态局部变量版本
若不想使用全局变量,可在函数内定义静态局部变量(具备内部链接性):
#include <iostream> #include <vector> struct LogParamScaler { }; template <LogParamScaler &T> struct QRange { }; // 封装静态变量的获取逻辑 LogParamScaler& getLogScaler() { static LogParamScaler instance; return instance; } int main() { QRange<getLogScaler()> qRange; // createObject<QRange<getLogScaler()>>(); }
方法2:改用模板类型参数(逻辑允许时)
如果不需要在模板中直接绑定特定对象,仅需关联LogParamScaler类型,可将模板改为接收类型参数:
#include <iostream> #include <vector> struct LogParamScaler { }; // 模板参数为类型,而非对象引用/指针 template <typename T> struct QRange { // 若需要持有对象,可在构造时传入 QRange(T& obj) : m_obj(obj) {} private: T& m_obj; }; int main() { LogParamScaler mLogParamScaler; QRange<LogParamScaler> qRange(mLogParamScaler); // 传入其他模板时使用类型QRange<LogParamScaler> // createObject<QRange<LogParamScaler>>(); }
这种方式更灵活,无需依赖全局/静态变量,但如果createObject要求在编译期绑定特定对象,则需回到方法1。
方法3:使用constexpr对象(C++11扩展)
部分编译器支持将constexpr对象作为模板参数,但C++11对constexpr对象限制较多,例如构造函数必须为constexpr:
#include <iostream> #include <vector> struct LogParamScaler { // C++11要求constexpr构造函数必须为空体或使用初始化列表 constexpr LogParamScaler() {} }; // constexpr对象具备外部链接性 constexpr LogParamScaler mLogParamScaler; template <const LogParamScaler &T> struct QRange { }; int main() { QRange<mLogParamScaler> qRange; // createObject<QRange<mLogParamScaler>>(); }
该方法依赖编译器对C++11constexpr的支持,兼容性略弱。
内容的提问来源于stack exchange,提问作者markzzz

