具有 32-bit/64-bit 整数重载的模板函数

Template function with 32-bit/64-bit integer overloads

实际函数 bar 应该是从文件中读取数据,其中写入的数据正好是 4 个字节或 8 个字节(unsigned int - DWORDDWORD64

void bar(DWORD64&);
void bar(DWORD&);

template<typename IntType>
void foo(IntType& out)
{
    bar(out);
}

int main()
{
    int a;
    foo(a); // Caller doesn't care
}

因为调用者可以传递任何整数类型(intLONGDWORDLONGLONG 或任何类型)——我想要一种技术,以便 foo 可以调用 32 位 bar 或 64 位 bar.

简而言之,就像:

template<typename IntType>
void foo(IntType& out)
{
       if(sizeof(out)==sizeof(DWORD))  // 32-bit
       {
             DWORD data;
             bar(data); // call DWORD version
             out = (IntType)data; // Ignore truncation etc.
       }
       else
       { 
             DWORD64 data;
             bar(data); // call DWORD64 version
             out = (IntType)data; // Ignore truncation etc.
       }
 }

显然,我希望在编译时解析“if”部分。 std::enable_if 之类的?

您可以使用std::conditional选择类型:

template<typename IntType>
void foo(IntType& out){
    using dword_t = typename std::conditional<sizeof(IntType) == sizeof(DWORD), DWORD, DWORD64>::type;
    dword_t data;
    bar(data);
    out = (IntType)data;
}

demo


你可以使用 C++17 constexpr if,但我不确定这种方法是否真的更具可读性:

template<typename IntType>
void foo(IntType& out)
{
       if constexpr(sizeof(out)==sizeof(DWORD))  // 32-bit
       {
             DWORD data;
             bar(data); // call DWORD version
             out = (IntType)data; // Ignore truncation etc.
       }
       else
       {
             DWORD64 data;
             bar(data); // call DWORD64 version
             out = (IntType)data; // Ignore truncation etc.
       }
 }

demo

您可以使用 std::conditional 来仅区分 sizeof(DWORD)sizeof(DWORD64)(因为您想要支持的不仅仅是这两种类型):

template<typename IntType>
void foo(IntType& out)
{
  typedef typename std::conditional<sizeof(IntType) == sizeof(DWORD), DWORD, DWORD64>::type RetType;
  RetType data;
  bar(data);
  out = static_cast<IntType>(data);
}

问题是,如果你传递了一个int16_t,你是想符号扩展、补零还是错误?

IMO 正确的做法是错误,所以这里有一个模板专业化的解决方案:

template <typename IntType>
void foo ( IntType * out ) = delete;

template <>
void foo ( uint64_t * out ) { bar ( * (DWORD64 *) out ); }

template <>
void foo ( int64_t * out ) { bar ( * (DWORD64 *) out ); }

template <>
void foo ( uint32_t * out ) { bar ( * (DWORD *) out ); }

template <>
void foo ( int32_t * out ) { bar ( * (DWORD *) out ); }

解决方案 1:SFINAE 和 std::enable_if

template<typename IntType, typename std::enable_if<sizeof(IntType) == 4>::type* = nullptr>
void foo(IntType& out)
{
    DWORD arg = out;
    bar(arg);
    out = arg;
}

template<typename IntType, typename std::enable_if<sizeof(IntType) == 8>::type* = nullptr>
void foo(IntType& out)
{
    DWORD64 arg = out;
    bar(arg);
    out = arg;
}

解决方案 2:委托给 class 和部分专业化:

template<typename IntType>
void foo(IntType& out)
{
    foo_helper<IntType>::call(out);
}

template <class IntType, std::size_t Size = sizeof(IntType)>
struct foo_helper;

template <class IntType>
struct foo_helper<IntType, 4>
{
  static void call(IntType &out)
  {
    DWORD arg = out;
    bar(arg);
    out = arg;
  }
};

template <class IntType>
struct foo_helper<IntType, 8>
{
  static void call(IntType &out)
  {
    DWORD64 arg = out;
    bar(arg);
    out = arg;
  }
};

两种解决方案都可以添加 static_cast 调味,特别是在作业 to/from arg.

附近