7

注意:我在`msg(long)` 和候选 `msg(int32_t)` 和 `msg(int64_t)` 等函数的模糊重载中添加了一个类似但大大简化的问题版本。该版本具有在单个文件中包含完整可编译示例的优点。

问题

我有一个 C 库,其功能如下

obj_from_int32(int32_t& i);
obj_from_int64(int64_t& i);
obj_from_uint32(uint32_t& i);
obj_from_uint64(uint64_t& i);

在这种情况下,类型int32_t不是那些std- 它们是实现定义的,在这种情况下是一个字符数组(在下面的示例中,我省略了转换 - 它不会改变关于将整数类型映射到基于整数类型的位数的特定函数)。

我有第二个 C++ 接口类,它有像这样的构造函数

MyClass(int z);
MyClass(long z);
MyClass(long long z);
MyClass(unsigned int z);
MyClass(unsigned long z);
MyClass(unsigned long long z);

注意,我不能用std::int32_t样式类型替换这个界面——如果可以的话,我不需要问这个问题;)

问题是如何obj_from_根据整数类型的位数调用正确的函数。

建议的解决方案

我提出了两个建议的解决方案,因为没有杀手解决方案浮现在列表的顶部,并且有一些被破坏了。

解决方案 1

Cheers 和 hth 提供。- 阿尔夫。从这一点开始的评论是我自己的 - 随时评论和/或编辑。

优点 - 相当简单(至少与 相比boost::enable_if) - 不依赖 3rd 方库(只要编译器支持tr1

*缺点** - 如果需要更多功能(如anotherObj_from_int32等),则需要更多代码

这个解决方案可以在下面找到 - 看看,它很漂亮!

解决方案 2

好处

  • 函数完成后,添加需要转换的ConvertFromIntegral新函数是微不足道的 - 只需编写一组重载 onint32_tint64_t无符号等价物。

  • 仅将模板的使用保留在一个地方,它们不会随着技术的重复使用而传播。

缺点

  • 可能过于复杂,使用boost::enable_if. 仅在一次出现的事实中有所缓解。

由于这是我自己的,我不能接受它,但如果你认为它很整洁,你可以投票赞成(显然有些人根本认为它很整洁,这就是反对它的原因,我想!)感谢所有谁贡献了想法!

该解决方案涉及从int、到long和的转换函数(对于无符号版本也类似)。这与另一组在 上重载的函数和无符号等效函数相结合。这两个函数可以组合,但是第一个转换函数构成了一个方便的实用程序集,可以重用,然后第二个函数集非常简单。long longint32_tint64_tint32_tint64_t

// Utility conversion functions (reuse wherever needed)
template <class InputT>
typename boost::enable_if_c<sizeof(InputT)==sizeof(int32_t) && boost::is_signed<InputT>::value,
 int32_t>::type ConvertFromIntegral(InputT z) { return static_cast<int32_t>(z); }

template <class InputT>
typename boost::enable_if_c<sizeof(InputT)==sizeof(int64_t) && boost::is_signed<InputT>::value, 
int64_t>::type ConvertFromIntegral(InputT z) { return static_cast<int64_t>(z); }

template <class InputT>
typename boost::enable_if_c<sizeof(InputT)==sizeof(uint32_t) && boost::is_unsigned<InputT>::value, 
uint32_t>::type ConvertFromIntegral(InputT z) { return static_cast<uint32_t>(z); }

template <class InputT>
typename boost::enable_if_c<sizeof(InputT)==sizeof(uint64_t) && boost::is_unsigned<InputT>::value, 
uint64_t>::type ConvertFromIntegral(InputT z) { return static_cast<uint64_t>(z); }

// Overload set (mock implementation, depends on required return type etc)
void* objFromInt32 (int32_t i)   { obj_from_int32(i); }
void* objFromInt64 (int64_t& i)  { obj_from_int64(i); }
void* objFromUInt32(uint32_t& i) { obj_from_uint32(i); }
void* objFromUInt64(uint64_t& i) { obj_from_uint64(i); }

// Interface Implementation
MyClass(int z) : _val(objFromInt(ConvertFromIntegral(z))) {}
MyClass(long z): _val(objFromInt(ConvertFromIntegral(z))) {}
MyClass(long long z): _val(objFromInt(ConvertFromIntegral(z))) {}
MyClass(unsigned int z): _val(objFromInt(ConvertFromIntegral(z))) {}
MyClass(unsigned long z): _val(objFromInt(ConvertFromIntegral(z))) {}
MyClass(unsigned long long z): _val(objFromInt(ConvertFromIntegral(z))) {}

解决方案的简化(单个可编译.cpp!)版本在“msg(long)”等函数的模糊重载与候选“msg(int32_t)”和“msg(int64_t)”中给出

4

5 回答 5

3

鉴于第 3功能……

void obj_from_int32( int32_bytes_t& i );
void obj_from_int64( int64_bytes_t& i );
void obj_from_uint32( uint32_bytes_t& i );
void obj_from_uint64( uint64_bytes_t& i );

您可以为内置类型调用“正确”的此类函数,如下所示:

template< int nBytes, bool isSigned >
struct ThirdParty;

template<>
struct ThirdParty< 4, true >
{
    template< class IntegralT >
    static void func( IntegralT& v )
    { obj_from_int32( v ) }    // Add whatever conversion is required.
};

// Etc., specializations of ThirdParty for unsigned and for 8 bytes.

template< class IntegralT >
void myFunc( IntegralT& v )
{ ThirdParty< sizeof( v ), std::is_signed< IntegralT >::value >::func( v ); }
于 2012-05-15T02:25:29.207 回答
2

正如我们在链接问题中发现的那样,这里的长是歧义的原因。线

MyClass(long z): _val(objFromInt(z)) {}

应该改为:

MyClass(long z): _val(sizeof(long) == 4 ? static_cast<int32_t>(z) : static_cast<int64_t>(z)))) {}

请注意,在 64 位 gcc 上使用 long long 可能会遇到类似的问题。

于 2012-05-14T09:02:11.370 回答
2

与其重载,不如说模式匹配呢?使用boost::enable_if和帮助模板来选择您正在寻找的操作类型?

像这样的东西:

#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_integral.hpp>
#include <iostream>

template <typename T, typename Dummy=void> struct helper;

// Handle signed integers of size 1 (8 bits)
template <typename T> struct helper<T, 
    typename boost::enable_if_c<
        boost::is_integral<T>::value && 
        (sizeof(T)==1) &&
        (static_cast<T>(-1) < static_cast<T>(0)) >::type>
{
    static void do_stuff(T const& ) {std::cout<<"signed, size 1"<<std::endl;}
};

// Handle unsigned integers of size 1 (8 bits)
template <typename T> struct helper<T, 
    typename boost::enable_if_c<
        boost::is_integral<T>::value &&
        (sizeof(T)==1) &&
        (static_cast<T>(-1) > static_cast<T>(0)) >::type>
{
    static void do_stuff(T const& ) {std::cout<<"unsigned, size 1"<<std::endl;}
};

// Handle signed integers of size 2 (16 bits)
template <typename T> struct helper<T, 
    typename boost::enable_if_c<
        boost::is_integral<T>::value && 
        (sizeof(T)==2) &&
        (static_cast<T>(-1) < static_cast<T>(0)) >::type>
{
    static void do_stuff(T const& ) {std::cout<<"signed, size 2"<<std::endl;}
};

// And so on and so forth....

// Use a function for type erasure:
template <typename T> void do_stuff(T const& value)
{
    helper<T>::do_stuff(value);
}

int main()
{
    do_stuff(static_cast<unsigned char>(0)); // "unsigned, size 1"
    do_stuff(static_cast<signed short>(0));  // "signed, size 2"
}

在http://ideone.com/pIhdq上更完整的列表(并证明它至少与 GCC 一起工作)。

编辑:或更简单,但覆盖面可能更少:(使用标准整数类型)

template <typename T> struct helper2;
template <> struct helper2<uint8_t> {static void do_stuff2(uint8_t ) {...}};
template <> struct helper2<int8_t> {static void do_stuff2(int8_t ) {...}};
template <> struct helper2<uint16_t> {static void do_stuff2(uint16_t ) {...}};
template <> struct helper2<int16_t> {static void do_stuff2(int16_t ) {...}};
// etc.
template <typename T> void do_stuff2(T value) {helper2<T>::do_stuff2(value);}
于 2012-05-15T00:42:50.103 回答
1

正如其他答案中所指出的,这可以在运行时使用if(sizeof(int)==sizeof(int32_t))样式分支轻松解决。要在编译时执行此操作, boost::enable_if可以使用。

template <class InputT>
typename boost::enable_if_c<sizeof(InputT)==sizeof(int32_t) && boost::is_signed<InputT>::value,
 int32_t>::type ConvertIntegral(InputT z) { return static_cast<int32_t>(z); }

template <class InputT>
typename boost::enable_if_c<sizeof(InputT)==sizeof(int64_t) && boost::is_signed<InputT>::value, 
int64_t>::type ConvertIntegral(InputT z) { return static_cast<int64_t>(z); }

template <class InputT>
typename boost::enable_if_c<sizeof(InputT)==sizeof(uint32_t) && boost::is_unsigned<InputT>::value, 
uint32_t>::type ConvertIntegral(InputT z) { return static_cast<uint32_t>(z); }

template <class InputT>
typename boost::enable_if_c<sizeof(InputT)==sizeof(uint64_t) && boost::is_unsigned<InputT>::value, 
uint64_t>::type ConvertIntegral(InputT z) { return static_cast<uint64_t>(z); }

任何需要将整数类型转换为int32_t,int64_t或简单调用的uint32_t地方:uint64_t

ConvertIntegral(long(5));  // Will return a type compatible with int32_t or int64_t

ConvertIntegral函数可以与int32_tint64_t重载集组合以获得完整的解决方案。或者,所示技术可以内置到重载集。

此外,可以通过禁用非整数类型来进一步增强上述功能。有关使用这些函数的完整示例,请参阅带有候选 `msg(int32_t)` 和 `msg(int64_t)` 的函数的模糊重载,例如 `msg(long)`

于 2012-05-15T00:57:35.373 回答
0

歧义很容易源于有符号和无符号类型的重载。例如,给定

void foo(unsigned int);
void foo(long);

然后是模棱两可的,因为从两者foo(0)的转换(或者可能是促销)并且排名相同。intunsigned intlong

如果您使用使用无符号类型的构造函数重载(并且您关心这一点),请选择一个符号,或者为每个符号编写两个重载集。

于 2012-05-14T06:49:54.487 回答