【问题标题】:Initializing a class template's member based on different conditions根据不同的条件初始化类模板的成员
【发布时间】:2026-02-06 07:50:01
【问题描述】:

我有一个类模板,它有两个构造函数,一个采用无符号类型,另一个采用自身的常量引用,两个构造函数在构造类的方式上具有相同的行为。

如何构造这个类有 3 种情况,这取决于它的类型的大小和传入的类型。我还希望通过类构造函数的初始化列表来初始化成员。

这个类看起来像这样:

template<typename T>
struct Foo {
    T data;

    template<typename P>
    Foo( const P val, const unsigned char c ) :
    data { static_cast<T>( val /* some operation done to val based on each case*/ ) } {
        assert( /* based on cases above,
                and each case has its own conditions to assert */ ); 
    }

    template<typename P>
    Foo( const Foo<P>& f, const unsigned char c ) :
    data{ static_cast<T>( Foo.val /* some operation done to val based on each case*/ ) } {
        assert( /* based on cases above, 
                and each case has its own conditions to assert */ );
};

这3种情况如下:

case 1: if( sizeof(P) > sizeof(T) )
           construct Foo<T> with these conditions,
           initialize member data in this specific way
           and assert accordingly
case 2: if( sizeof(T) > sizeof(P) )
           construct Foo<T> with these conditions,
           initialize member data in this specific way
           and assert accordingly
case 3: if ( sizeof(T) == sizeof(P) )
           construct Foo<T> with these conditions,
           initialize member data in this specific way
           and assert accordingly

有没有简单的方法来做到这一点?请记住,成员正在通过构造函数的初始化列表进行初始化,但数据成员将以不同的方式初始化,这取决于上述 3 种情况。

我不知道我该如何解决这个问题。这里的所有信息都应该在编译时可用,所以我不明白为什么会有问题,但我不知道如何设置构造函数来获得这个功能。此外,这甚至可以做到吗?


编辑

TP 类型的一些背景知识:TP 都是以下任何一种:

类型:以字节为单位的大小:以位为单位的大小:

  • std::uint8_t1字节8位
  • std::uint16_t 2 字节 16 位
  • std::uint32_t 4 字节 32 位
  • std::uint64_t 8 字节 64 位

一旦我们根据两个大小的比较定义了 3 个条件,断言的工作方式如下:传入的 unsigned char 也是断言中条件的一部分,并且它有一个可以使用的值范围是。这是一个表格:我将用idx 表示的无符号字符。

using u8  = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;

if ( sizeof(P) > sizeof(T) )
   // comparision formula:
   // idx <= (((sizeof(P) / sizeof(T)) - 1)
   // Foo<T = u8> From: Foo<P = u16> idx[0,1], Foo<P =u32> idx[0,3], Foo<P = u64> idx[0,7]
   // Foo<T = u16> From: Foo<P = u32> idx[0,1], Foo<P = u64> idx[0,3]
   // Foo<T = u32> From: Foo<P = u64> idx[0,1]

if ( sizeof(T) > sizeof(P) )
   // comparision formula:
   // idx <= (((sizeof(T) / sizeof(P)) - 1)
   // Foo<T = u16> From: Foo<P = u8> idx[0,1]
   // Foo<T = u32> From: Foo<P = u8> idx[0,4], Foo<P = u16> idx[0,1]
   // Foo<T = u64> From: Foo<P = u8> idx[0,7], Foo<P = u16> idx[0,3], Foo<P = u32> idx[0,1]

if ( sizeof(P) == sizeof(T) ) {
   // no conditional checks
   // no assertion
   // in this case idx is not used and is not a part of any calculation
   // the two sizes match so it's just a normal initialization.

【问题讨论】:

    标签: c++ templates constructor c++17 list-initialization


    【解决方案1】:

    这是一种if constexpr 无法使用的情况,您必须求助于 SFINAE。首先,将Foo&lt;P&gt;构造函数的构造委托给P构造函数:

    template<typename P>
    Foo(const Foo<P>& f, const unsigned char c) : Foo(f.val, c) {}
    

    P 构造函数只需要三个选项,根据P 的大小启用其中一个即可:

    template<typename P, std::enable_if_t<(sizeof(P) > sizeof(T))>* = nullptr>
    Foo(P val, const unsigned char c) : data(...) {}
    
    template<typename P, std::enable_if_t<sizeof(P) == sizeof(T)>* = nullptr>
    Foo(P val, const unsigned char c) : data(...) {}
    
    template<typename P, std::enable_if_t<(sizeof(P) < sizeof(T))>* = nullptr>
    Foo(P val, const unsigned char c) : data(...) {}
    

    如果您希望P 构造函数具有相同的断言,而不管P 的大小,您还可以添加额外的委托层。

    【讨论】:

    • 我将编辑上面关于断言如何工作的问题。
    • 我完成了编辑,以提供有关如何执行构造的更多信息。
    • 但是我想我开始理解或看到 SFINAE 的模式以及 std::enable_if_t 的使用方式。
    • 我创建了一个虚拟类来复制您在上面所做的事情,但我收到了 Visual Studio 2017 编译器错误 C2059:语法错误:sizeof
    • 我想我已经完成了这个shell,我还没有做任何测试。一旦我在一个简单的案例中对此进行了测试;我应该能够将它集成到我的实际课程中。