如何在类实例化的基础上初始化constexpr静态类成员



基本上,我想允许类Foo的客户端在实例化Foo时使用基于模板类型参数的任意值来定义其static constexpr成员变量。

这是一个MRE:

#include <iostream>
#include <concepts>

template < std::unsigned_integral size_type,
class Allocator = std::allocator<char> >
class Foo
{
public:
static constexpr size_type constant1 { 20 };
static constexpr size_type constant2 { 30 };
void dummy_func( ) const
{
std::cout << constant1 << ' '
<< constant2 << 'n';
}
};
int main( )
{
Foo<std::uint32_t> fooInstance1;
fooInstance1.dummy_func( ); // prints: 20 30
// I want these static members to be initialized
// by the client but this dummy solution does not work
// Foo<std::uint64_t>::constant1 { 120 };
// Foo<std::uint64_t>::constant2 { 130 };
Foo<std::uint64_t> fooInstance2;
fooInstance2.dummy_func( ); // should print: 120 130
}

请注意,值20

30我也研究过这个类似的问题,但无法使其适用于上述具体情况。

我想到的一种可能的方法是使用变量模板。但我不知道怎么做。另一个是Foo的显式实例化。或者可能是部分实例化?

现在我想提到的是,这个类显然应该能够进入一个头文件,然后被包括在任何需要实例化和使用它的源文件中

有没有一种简单的方法可以做到这一点?

我的假设是Foo类有很多static constexpr变量,Foo类的编写器不喜欢编写长模板签名

所以我希望下面的解决方案对于Foo类编写器来说足够简单和可扩展

template <std::unsigned_integral size_type, size_type... args>
class Foo
{
public:
static constexpr size_type constant1{ std::get<0>(internal_values) };
static constexpr size_type constant2{ std::get<1>(internal_values) };
void dummy_func() const
{
std::cout << constant1 << ' ' << constant2 << std::endl;
}
private:
static constexpr std::tuple<size_type, size_type> internal_values{ std::tuple<decltype(args)...>(std::forward<size_type>(args)...) };
};

对于这个类的使用,你可以只写简单的代码,比如下面的

int main()
{
auto fooInstance1 = Foo<std::uint32_t, 20, 30>();
fooInstance1.dummy_func(); // prints: 20 30
auto fooInstance2 = Foo<std::uint64_t, 200, 300>();
fooInstance2.dummy_func(); // prints: 200 300
}

似乎constexpr值在声明时必须初始化。因此,看起来您要么必须进行专门化(可能是通过添加注释中建议的模板参数,要么从该类派生(。

话虽如此,如果正则静态常数有效,你也可以做:

#include <iostream>
#include <concepts>
template < std::unsigned_integral size_type,
class Allocator = std::allocator<char> >
class Foo
{
public:
// (1) const is used here instead of constexpr
static const size_type constant1;
static const size_type constant2;
void dummy_func() const
{
std::cout << constant1 << ' '
<< constant2 << 'n';
}
};
// (2) Definitions cannot be inside a function.
// (3) Proper syntax must be used to define a constant out-of-class.
// (4) You must provide definition for both 32 and 64 bit since you use both.
// (5) You should have a single definition per type.
template <>
const std::uint32_t Foo<std::uint32_t>::constant1{ 120 };
template <>
const std::uint32_t Foo<std::uint32_t>::constant2{ 130 };
template <>
const std::uint64_t Foo<std::uint64_t>::constant1{ 220 };
template <>
const std::uint64_t Foo<std::uint64_t>::constant2{ 230 };

int main()
{
Foo<std::uint32_t> fooInstance1;
fooInstance1.dummy_func(); // prints: 120 130
Foo<std::uint64_t> fooInstance2;
fooInstance2.dummy_func(); // prints: 220 230
}

这样,您将得到以下输出:

120 130
220 230

为什么要指定分配器参数?您打算为每个分配器指定不同的常量吗?

看起来过于复杂。如果客户端同时设置两个常量,为什么不编写带参数的构造函数?

#include <iostream>
template <typename size_type,
class Allocator = std::allocator<char> >
class Foo
{
public:
Foo(size_type c1=20, size_type c2=30) :
constant1(c1),
constant2(c2) {}
const size_type constant1;
const size_type constant2;
void dummy_func() const
{
std::cout << constant1 << ' '
<< constant2 << 'n';
}
};

int main()
{
Foo<std::uint32_t> fooInstance1;
fooInstance1.dummy_func(); // prints: 20 30

Foo<std::uint64_t> fooInstance2(120, 130);
fooInstance2.dummy_func(); // should print: 120 130
}

我去掉了<concepts>的东西,它是C++20,我只能使用C++14。我不明白的是:

这些常量是否特定于size_type类型

如果是,那么Foo类的Client(User(为什么要设置这些值呢?您应该更好地为所有需要的类型提供Foo类的非模板实现,并使用正确的常量。(当然,每个实现都可能继承自您的模板类(

这些常量是否独立于size_type的类型

如果是,为什么它们应该是静态constexpr?如果两个客户端想要用不同的常量实例化您的类,该怎么办?

你应该总是有充分的理由考虑写";静态";。静态大多是邪恶的(我知道……这只是我的观点(。

最新更新