类含非constexpr std::array时,如何将std::array.size()作为模板参数?
std::array::size()的问题 类student包含一个名为name的std::array<char, 15>成员和一个整数age。该类的成员函数encode尝试以name.size()作为模板参数调用全局模板函数encode。
代码示例
//main.cpp #include <iostream> #include <array> template <unsigned long num1> unsigned long encode(unsigned long num2){ return num1 + num2; } struct student { std::array<char, 15> name; int age; student(const std::array<char, 15>& name, int age): name(name), age(age) {} unsigned long encode(){ return ::encode<name.size()>(age); } }; int main(){ std::array<char, 15> name = {"Tim"}; student Tim(name, 17); std::cout << Tim.encode(); }
编译错误信息
>g++ main.cpp -std=c++11 main.cpp: In member function 'long unsigned int student::encode()': main.cpp:22:43: error: use of 'this' in a constant expression 22 | return ::encode<name.size()>(age); | ^ main.cpp:22:45: error: no matching function for call to 'encode<((student*)this)->student::name.std::array<char, 15>::size()>(int&)' 22 | return ::encode<name.size()>(age); | ~~~~~~~~~~~~~~~~~~~~~^~~~~ main.cpp:9:15: note: candidate: 'template<long unsigned int num1> long unsigned int encode(long unsigned int)' 9 | unsigned long encode(unsigned long num2){ | ^~~~~~ main.cpp:9:15: note: template argument deduction/substitution failed: main.cpp:22:45: error: use of 'this' in a constant expression 22 | return ::encode<name.size()>(age); | ~~~~~~~~~~~~~~~~~~~~~^~~~~ main.cpp:22:42: note: in template argument for type 'long unsigned int' 22 | return ::encode<name.size()>(age);
用户疑问
是否必须使用::encode<15>(age)来解决此问题?因为我认为std::array的一大优势是能携带自身大小,无需额外存储或硬编码大小。
g++ version: 14.1.0
问题原因
模板参数必须是编译期常量表达式。虽然std::array::size()在C++11及以后是constexpr函数,但在类的非静态成员函数中,访问成员变量name需要依赖this指针,而this不属于编译期常量,因此name.size()无法直接作为模板参数传递。
无需硬编码的解决方案
你可以通过以下几种方式避免硬编码大小:
利用
std::array的编译期静态常量std::array自带编译期可用的静态size成员,或者通过std::tuple_size获取大小,在成员函数中这样写:unsigned long encode(){ // 方式1:直接引用std::array的静态size return ::encode<decltype(name)::size()>(age); // 方式2:用std::tuple_size获取 // return ::encode<std::tuple_size<decltype(name)>::value>(age); }将类模板化,让大小成为模板参数
如果希望student类适配不同大小的std::array,可以把类定义为模板:template <std::size_t N> struct student { std::array<char, N> name; int age; student(const std::array<char, N>& name, int age): name(name), age(age) {} unsigned long encode(){ return ::encode<N>(age); } }; int main(){ std::array<char, 15> name = {"Tim"}; student<15> Tim(name, 17); std::cout << Tim.encode(); }这种方式既避免了硬编码,还让类具备了通用性。
C++20及以后:constexpr成员函数
升级到C++20后,可以将成员函数声明为constexpr,此时this在constexpr上下文中是编译期常量,就能直接使用name.size():constexpr unsigned long encode() const { return ::encode<name.size()>(age); }
总结
不需要硬编码15,通过上述方法就能利用std::array的编译期大小信息,同时满足模板参数的常量表达式要求。
内容的提问来源于stack exchange,提问作者timmy george

