根据类型模板的类型参数

时间:2019-05-13 15:37:00

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

在使用类模板时,是否可以通过推断其类型来指定特定大小?这是一个伪代码示例,用来表示我要询问的内容。

#include <bitset>
#include <cstdint>

namespace xxx {

#define BYTE  8
#define WORD  16
#define DWORD 32
#define QWORD 64

    typedef std::uint8_t   u8;
    typedef std::uint16_t u16;
    typedef std::uint32_t u32;
    typedef std::uint64_t u64;

    template<typename Type, u16 BitCount>
    struct Register {
    };
}

我不知道我可以使用部分专业化还是完全专业化,我也不知道该怎么做。我想做的是以下事情:

  
      
  • 如果Type == u8,则BitCount自动== BYTE8
  •   
  • 如果Type == u16,则BitCount自动== WORD16
  •   
  • 如果Type == u32,则BitCount自动== DWORD32
  •   
  • 如果Type == u64,则BitCount自动== QWORD64
  •   

这样做的原因是,当涉及到上面的类及其成员时,我尚未显示但在此处将要显示的是,它的成员之一是std::bitset

template<typename Type, u16 BitCount>
struct Register {
   Type value;
   std::bitset<BitCount> 
};

是的,我知道我可以这样实例化它们:

void someFunc() {
    Register<u8, 8> r8;
    Register<u16, 16> r16;
}

但是我希望能够专门化它们,这样您就不必传入任何参数类型,我希望可以通过传入的参数类型来推论它们,或者如果只是传递一个类型,那么大小部分是自动的。这是首选

void someFunc() {
    Register<u8> r8;
} 

// the class would automatically use `8` for its `std::bitset<8>` member.

始终存在其基础类型和位大小的一对一对应关系。

这种实例化无效:

Register<u32, 64> reg; // invalid type u32 can only be 32...

是否有通过专门化,继承,多态等方法来做到这一点?

3 个答案:

答案 0 :(得分:5)

您可以只使用usings:

using u8Register = Register<u8, BYTE>;
using u16Register = Register<u16, WORD>;
using u32Register = Register<u32, DWORD>;
using u64Register = Register<u64, QWORD>;

或者,如果这些类型与其字节大小一一对应,则可以使用sizeof

#include <limits.h>
template<typename Type, u16 BitCount = sizeof(Type) * CHAR_BIT>
struct Register {
   Type value;
   std::bitset<BitCount> 
};

或者您可以从Register继承来专门化。

答案 1 :(得分:4)

听起来您实际上并不需要第二个模板参数。您只需要一个模板参数,并直接从中确定位数:

template <typename Type>
struct Register {
    static constexpr auto Bits = sizeof(Type) * CHAR_BIT;
    // ...
};

或者,如果您想对此逻辑进行某种概括:

template <typename> struct BitCount;
template <> struct BitCount<uint8_t>  : integral_constant<size_t, 8> {};
template <> struct BitCount<uint16_t> : integral_constant<size_t, 16> {};
// ...

template <typename Type>
struct Register {
    static constexpr auto Bits = BitCount<T>::value;
    // ...
};

答案 2 :(得分:-1)

解决我的问题;他们俩对此都给出了类似的答案,但我认为Barry的表达方式更具表现力,因此我必须为他的回答功劳。

现在,对于Sombrero,他的确为我提供了using Reg# = Register<T>的帮助,因此我不必通过传递它们的类型参数来调用这些模板。

基本上可以归结为:

static constexpr auto Bits = sizeof(Type) * CHAR_BIT;

感谢您为我提供的帮助。


使用来自用户BarrySombreroChicken的反馈,我能够做到这一点:

main.cpp

#include <iostream>
#include "Register.h"

int main() {
using namespace vpc;

    u8  valA = 8;
    u16 valB = 16;
    u32 valC = 32;
    u64 valD = 64;

    Reg8 r8A(valA);
    Reg8 r8B(valB);
    Reg8 r8C(valC);
    Reg8 r8D(valD);

    Reg16 r16A(valA);
    Reg16 r16B(valB);
    Reg16 r16C(valC);
    Reg16 r16D(valD);

    Reg32 r32A(valA);
    Reg32 r32B(valB);
    Reg32 r32C(valC);
    Reg32 r32D(valD);

    Reg64 r64A(valA);
    Reg64 r64B(valB);
    Reg64 r64C(valC);
    Reg64 r64D(valD);

    std::cout << r8A << r8B << r8C << r8D;
    std::cout << r16A << r16B << r16C << r16D;
    std::cout << r32A << r32B << r32C << r32D;
    std::cout << r64A << r64B << r64C << r64D;

    return EXIT_SUCCESS;
}

输出

Reg8(8)
hex: 0x08
bin: 00001000

Reg8(16)
hex: 0x10
bin: 00010000

Reg8(32)
hex: 0x20
bin: 00100000

Reg8(64)
hex: 0x40
bin: 01000000

Reg16(8)
hex: 0x0008
bin: 0000000000001000

Reg16(16)
hex: 0x0010
bin: 0000000000010000

Reg16(32)
hex: 0x0020
bin: 0000000000100000

Reg16(64)
hex: 0x0040
bin: 0000000001000000

Reg32(8)
hex: 0x00000008
bin: 00000000000000000000000000001000

Reg32(16)
hex: 0x00000010
bin: 00000000000000000000000000010000

Reg32(32)
hex: 0x00000020
bin: 00000000000000000000000000100000

Reg32(64)
hex: 0x00000040
bin: 00000000000000000000000001000000

Reg64(8)
hex: 0x0000000000000008
bin: 0000000000000000000000000000000000000000000000000000000000001000

Reg64(16)
hex: 0x0000000000000010
bin: 0000000000000000000000000000000000000000000000000000000000010000

Reg64(32)
hex: 0x0000000000000020
bin: 0000000000000000000000000000000000000000000000000000000000100000

Reg64(64)
hex: 0x0000000000000040
bin: 0000000000000000000000000000000000000000000000000000000001000000

注册。h

#pragma once

#include <algorithm>
#include <bitset>

#include <string>
#include <vector> // include for typedefs below.

namespace vpc {
    typedef std::int8_t  i8;
    typedef std::int16_t i16;
    typedef std::int32_t i32;
    typedef std::int64_t i64;

    typedef std::uint8_t u8;
    typedef std::uint16_t u16;
    typedef std::uint32_t u32;
    typedef std::uint64_t u64;

    const u16 BYTE = 0x08;
    const u16 WORD = 0x10;
    const u16 DWORD = 0x20;
    const u16 QWORD = 0x40;

    typedef std::bitset<BYTE>  Byte;
    typedef std::bitset<WORD>  Word;
    typedef std::bitset<DWORD> DWord;
    typedef std::bitset<QWORD> QWord;

    template<typename Ty>
    struct Register_t {
        static constexpr u16 BitCount = sizeof(Ty) * CHAR_BIT;
        Ty currentValue;
        Ty previousValue;
        std::bitset<BitCount> bits;

        Register_t() : currentValue{ 0 }, previousValue{ 0 }, bits{ 0 }{}

        template<typename U>
        explicit Register_t(U val) : currentValue{ static_cast<Ty>(val) }, previousValue{ 0 }, bits{ currentValue } {}
    };

    template<typename Ty>
    struct Register : public Register_t<Ty> {
        Register() = default;
        explicit Register(Ty val) : Register_t<Ty>( val ) {}
    };

    using Reg8  = Register<u8>;
    using Reg16 = Register<u16>;
    using Reg32 = Register<u32>;
    using Reg64 = Register<u64>;

    std::ostream& operator<<(std::ostream& os, const Reg8&  reg);
    std::ostream& operator<<(std::ostream& os, const Reg16& reg);
    std::ostream& operator<<(std::ostream& os, const Reg32& reg);
    std::ostream& operator<<(std::ostream& os, const Reg64& reg);

} // namespace vpc

Register.cpp

#include "Register.h"

#include <iostream>
#include <iomanip>

namespace vpc {

    std::ostream& operator<<(std::ostream& os, const Reg8& r) {
        os << "Reg8(" << +r.currentValue << ")\n"
            << "hex: " << "0x" << std::uppercase
            << std::setfill('0') << std::setw(2) << std::hex
            << +r.currentValue << std::dec << '\n'
            << "bin: " << r.bits << '\n' << std::endl;
        return  os;
    }
    std::ostream& operator<<(std::ostream& os, const Reg16& r) {
        os << "Reg16(" << r.currentValue << ")\n"
            << "hex: " << "0x" << std::uppercase
            << std::setfill('0') << std::setw(4) << std::hex
            << r.currentValue << std::dec << '\n'
            << "bin: " << r.bits << '\n' << std::endl;
        return  os;
    }
    std::ostream& operator<<(std::ostream& os, const Reg32& r) {
        os << "Reg32(" << r.currentValue << ")\n"
            << "hex: " << "0x" << std::uppercase
            << std::setfill('0') << std::setw(8) << std::hex
            << r.currentValue << std::dec << '\n'
            << "bin: " << r.bits << '\n' << std::endl;
        return  os;
    }
    std::ostream& operator<<(std::ostream& os, const Reg64& r) {
        os << "Reg64(" << r.currentValue << ")\n"
            << "hex: " << "0x" << std::uppercase
            << std::setfill('0') << std::setw(16) << std::hex
            << r.currentValue << std::dec << '\n'
            << "bin: " << r.bits << '\n' << std::endl;
        return  os;
    }

} // namespace vpc

这就是我想要的。