【问题标题】:From template parameter to constructor argument从模板参数到构造函数参数
【发布时间】:2021-03-16 17:42:35
【问题描述】:

C++17(或更早版本但不是 c++20)是否允许这样做?

我需要一个 type_traited 条件类,例如具有内部 32 或 64 无符号整数存储的位集,具体取决于模板参数 N 是否小于 32 或更大(请忘记超过 64 位)。

但约束是最终为所有可能的情况实现两个且仅两个类。下一个源代码使用静态和运行时间断言来定义问题:

Coliru 链接:http://coliru.stacked-crooked.com/a/d53a5b00bd828fb5

#include <cassert>
#include <iostream>
#include <type_traits>

struct bitset32
{
    bitset32() : bits(0) { }
    bitset32(int _bits) : bits(_bits) { }

    const int bits;
    uint32_t value;
};

struct bitset64
{
    bitset64() : bits(0) { }
    bitset64(int _bits) : bits(_bits) { }
    
    const int bits;
    uint64_t value;
};

template <int N>
using bitset = std::conditional_t<(N<=32), bitset32, bitset64>;

int main ()
{
    static_assert(std::is_same<bitset<1>, bitset<2>>::value);
    static_assert(std::is_same<bitset<33>, bitset<34>>::value);
    static_assert(!std::is_same<bitset<1>, bitset<33>>::value);

    bitset<1> var1;
    bitset<15> var2;
    bitset<32> var3;
    bitset<64> var4;

    assert(var1.bits == 1);
    assert(var2.bits == 15);
    assert(var3.bits == 32);
    assert(var4.bits == 64);
}

欢迎任何解决方案,即使它更改了基本类型并使用继承或任何其他必要的机制,但请不要使用模板函数返回template&lt;int N&gt; make_bitset { return Bitset&lt;N&gt;(N); } 样式的对象,因为它需要实现变量使用这个构造 Bitset&lt;N&gt; variable_name

【问题讨论】:

  • 不可能。如果bitset&lt;1&gt;bitset&lt;15&gt; 是同一类型,则需要将值传递给构造函数。如果没有...将值传递给构造函数,这是不可能的。
  • 是的,没错,但我想知道在现代 c++ 中,模板是否可以以我不知道的方式将参数传递给构造函数,但也许你是对的,这是不可能的
  • std::is_same&lt;bitset&lt;1&gt;, bitset&lt;2&gt;&gt;::value 要求 bitset&lt;1&gt;bitset&lt;2&gt; 具有完全相同的最派生类型,因此位计数不能在该类型中。并且没有信息传递给构造函数,因此位计数 必须 在类型中。我们可以放松is_same 检查吗?
  • 这也是XY problem。为什么要达到这些要求?你需要修复那个
  • @Pablo:你要求的东西是不可能的。 This 与您将得到的一样接近。

标签: c++ templates c++17 template-argument-deduction


【解决方案1】:

一个新假设,基于CTAD

#include <cstdint>
#include <iostream>
#include <type_traits>

template <std::size_t>
struct BitSet;

template <>
struct BitSet<32u>
 {
   template <typename ... Ts>
   BitSet (Ts...) {}

   std::uint32_t value;
 };

template <>
struct BitSet<64u>
 {
   template <typename ... Ts>
   BitSet (Ts...) {}

   std::uint64_t value;
 };

template <typename ... Ts>
BitSet (Ts...) -> BitSet<(32 < sizeof...(Ts) ? 64u : 32u)>;


int main()
 {
   auto b1 = BitSet{1};
   auto b2 = BitSet{2};
   auto b3 = BitSet{0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
                    10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
                    20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
                    30, 31, 32, 33, 34, 35, 36, 37, 38, 39};
   auto b4 = BitSet{0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
                    10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
                    20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
                    30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
                    40, 41, 42, 43, 44, 45, 46, 47, 48, 49};

   static_assert( std::is_same_v<decltype(b1), decltype(b2)> );
   static_assert( std::is_same_v<decltype(b1), BitSet<32u>> );
   static_assert( std::is_same_v<decltype(b3), decltype(b4)> );
   static_assert( std::is_same_v<decltype(b3), BitSet<64u>> );
 }

【讨论】:

  • 感谢您的回答,但这不是我要找的。也许我的问题不清楚。我想要的是:一个模板 BitSet 类,它接受 N 位作为模板参数,并为 1 到 32 之间的所有 N“生成”相同的类类型,为 33 到 64 之间的所有 N“生成”相同的类型类。具有相同的类型我要求std::is_same(BitSet&lt;1&gt;, BitSet&lt;2&gt;) = true上课。那么,内部存储必须是uint32和uint64,取决于N,最后更难的是,模板必须能够将bits成员变量初始化为N。这在当前的C++中是不可能的吗?
  • @Pablo - 是的......我误解了你的问题。对于std::is_same(BitSet&lt;1&gt;, BitSet&lt;2&gt;),不,在当前的 C++ 中是不可能的。您可以使用 using: template &lt;std::size_t ... Is&gt; using BitSet = ClassBitSet&lt;sizeof...(Is)&gt;; 模拟它,但从整数列表中,您只保留包的长度。
  • @Pablo 但是模板类有什么问题,在我的示例中有两个专业化,其中模板参数是通过 CTAD “确定”的?你可以得到(我想)std::is_same_v&lt;decltype(BitSet(1)), decltype(BitSet(2))&gt; == true,因为(CTAD)BitSet(1) 变成了BitSet&lt;32&gt;(1)BitSet(2) 变成了BitSet&lt;32&gt;(2)。但是BitSet(0, 1, ...., 87)(87 个参数)变成了BitSet&lt;64&gt;(0, 1, ...., 87)
  • @Pablo - 我已经更改了答案以表明我的意思。
  • @Pablo - 不,这是不可能的。 CTAD 允许根据构造函数的参数“推断”(在这种情况下决定)模板参数(一个假构造函数,当显式时(在这种情况下))。我在明确的 CTAD 中说过:如果构造函数的参数个数(即sizeof...(Ts))大于32u,则模板参数为64u,否则为32u。但这一切都取决于构造函数的参数数量。
【解决方案2】:

您可以制作 bitset32bitset64 之类的模板

template <std::size_t BitsUsed>
struct bitset32
{
    bitset32() : bits(BitsUsed) { }
    bitset32(int _bits) : bits(_bits) { }

    const int bits;
    uint32_t value;
};

template <std::size_t BitsUsed>
struct bitset64
{
    bitset64() : bits(BitsUsed) { }
    bitset64(int _bits) : bits(_bits) { }
    
    const int bits;
    uint64_t value;
};

现在默认构造函数将引入正确的位数。然后你只需要修改你的别名就像

template <int N>
using bitset = std::conditional_t<(N<=32), bitset32<N>, bitset64<N>>;

你得到你要求的行为。

如果您想将bitset&lt;5&gt; 存储在与bitset&lt;32&gt; 相同的容器中,这将不起作用,因为不同的模板实例化创建不同的类型。

【讨论】:

  • 感谢您的回答,但这不是我想要的行为。问题的关键是只实现 2 个最终类,与 [1, 32] 和 [33, 64] 范围内的位数无关。我想知道这是否可能或根本不可能
  • @Pablo:不,考虑到您提出的限制,这是 100% 不可能的,因为位数必须同时是类型的一部分,而不是类型的一部分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-01-29
  • 2023-01-30
  • 2023-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-02
相关资源
最近更新 更多