基本上,我想允许类
Foo
的客户根据实例化static constexpr
时传递给它的模板类型参数使用任意值定义其Foo
成员变量。
这是一个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
版本具有哪些值因为它的constant1
和constant2
.
我也看过this类似的问题,但无法使其适用于上述特定情况。
我想到的一种可能方法是使用 变量模板 代替。但我不确定如何。另一个是 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 类的客户端(用户)应该设置这些值?您最好为具有正确常量的所有需要的类型提供 Foo 类的非模板实现。 (当然每个实现都可能继承自你的模板类)
这些常量是否独立于size_type的类型?
如果是,那为什么它们应该是 static constexpr?如果两个客户端想用不同的常量实例化你的类怎么办?
你应该总是有充分的理由去考虑写“静态”这个词。静电主要是邪恶的(我知道......这只是我的意见)。