每个类实例化基础如何初始化constexpr静态类成员?

问题描述 投票:0回答:3

基本上,我想允许类

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
}

请注意,值 2030 用于演示目的,不需要在课堂内,因为我想强迫客户自己决定他们希望他们的

Foo
版本具有哪些值因为它的
constant1
constant2
.

我也看过this类似的问题,但无法使其适用于上述特定情况。

我想到的一种可能方法是使用 变量模板 代替。但我不确定如何。另一个是 Foo 的显式实例化。或者可能是部分实例化?

现在我想提一下,该类显然应该能够进入头文件,然后包含在需要实例化和使用它的任何源文件中。

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

c++ c++20 constexpr class-template explicit-instantiation
3个回答
2
投票

我的假设是 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
}

1
投票

看来

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

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


0
投票

似乎过于复杂。如果客户要设置两个常量,为什么不写一个带参数的构造函数?

#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?如果两个客户端想用不同的常量实例化你的类怎么办?

你应该总是有充分的理由去考虑写“静态”这个词。静电主要是邪恶的(我知道......这只是我的意见)。

© www.soinside.com 2019 - 2024. All rights reserved.