如何在源文件中定义类的静态const成员以指定数组大小?
我定义了如下类:
Foo.hpp:
#pragma once #include <cstdint> class Foo { static const std::size_t Size; std::uint32_t buffer[Size]; };
我希望在源文件中计算Size的值,避免在头文件中引入计算所需的依赖文件,于是写了如下源文件:
Foo.cpp:
#include "Foo.hpp" #include "Lib1.hpp" // -> #define Size1 1 #include "Lib2.hpp" // -> #define Size2 2 const std::size_t Foo::Size = Size1 + Size2;
编译后出现错误:
In file included from src\Foo.cpp:1: include/Foo.hpp:6:24: error: size of array 'buffer' is not an integral constant-expression 30 | std::uint32_t buffer[Size]; | ^~~~
请问是否可以外部声明类的静态const成员?如果可以,该如何实现?
你这么写肯定行不通——编译器在处理头文件Foo.hpp时,必须知道Size的具体数值才能确定数组buffer的大小,毕竟数组大小要求是编译期常量。而你把Size的定义放在源文件里,头文件里只有一个声明,编译器在编译头文件时根本拿不到Size的实际值,自然会报错。
要实现「不在头文件引入依赖,同时让数组大小由源文件计算的常量决定」的需求,可参考以下几种方案:
方案1:用std::array+类模板延迟确定大小(C++17及以上)
把固定数组换成std::array,通过类模板把大小的确定延迟到源文件:
Foo.hpp:
#pragma once #include <cstdint> #include <array> // 先定义通用模板类 template<std::size_t S> class FooImpl { protected: std::array<std::uint32_t, S> buffer; }; // 声明Foo是FooImpl的特化版本 class Foo;
Foo.cpp:
#include "Foo.hpp" #include "Lib1.hpp" #include "Lib2.hpp" // 在这里计算大小并完成特化 constexpr std::size_t FooSize = Size1 + Size2; class Foo : public FooImpl<FooSize> {};
这种方式把大小计算完全放到源文件,头文件不需要引入任何依赖,同时保证数组大小是编译期确定的,没有动态内存开销。
方案2:改用动态内存分配(兼容性最好)
如果能接受堆内存,把数组换成智能指针,在构造函数里分配内存:
Foo.hpp:
#pragma once #include <cstdint> #include <memory> class Foo { static const std::size_t Size; std::unique_ptr<std::uint32_t[]> buffer; public: Foo(); ~Foo() = default; };
Foo.cpp:
#include "Foo.hpp" #include "Lib1.hpp" #include "Lib2.hpp" const std::size_t Foo::Size = Size1 + Size2; Foo::Foo() : buffer(std::make_unique<std::uint32_t[]>(Size)) {}
这种写法完全不需要在头文件知道Size的值,编译错误直接消失,而且unique_ptr会自动管理内存,不用手动释放。
方案3:把大小计算隔离到单独头文件(C++11及以上)
如果坚持要用静态数组,可以把大小计算放到一个只包含必要依赖的小文件里,再在Foo.hpp中引入这个文件:
SizeCalc.hpp:
#pragma once #include "Lib1.hpp" #include "Lib2.hpp" constexpr std::size_t FooSize = Size1 + Size2;
Foo.hpp:
#pragma once #include <cstdint> #include "SizeCalc.hpp" class Foo { static constexpr std::size_t Size = FooSize; std::uint32_t buffer[Size]; };
Foo.cpp:
#include "Foo.hpp" // C++17及以上不需要这行,C++11/14必须显式定义静态constexpr成员 constexpr std::size_t Foo::Size;
这种方式把依赖隔离在SizeCalc.hpp里,Foo.hpp只引入这个小文件,避免直接引入Lib1.hpp和Lib2.hpp,也算间接实现了你的需求。
内容的提问来源于stack exchange,提问作者GURKE

