假设有以下API:
typedef void callback_t(void* data);
void addCallback(handle_t h, callback_t callback, void* data);
我想将此API包装到更高阶的C ++接口:
template<class F, bool emplace = IsEmplaceable<F>::value>
struct MakeCallback;
class Handle
{
template<class F>
void addCallback(F f)
{
::addCallback(_h, MakeCallback<F>::f, MakeCallback<F>::create(f));
}
handle_t _h;
};
以便用户可以传递任何可调用对象(例如lambda函数)。
我想应用小对象优化来避免动态分配(例如,对于空的lambdas),特征IsEmplaceable<F>
决定F
是否可以放在void*
中。
对于不可复制的F
,MakeCallback
可以如下实现:
template<class F>
struct MakeCallback<F, false>
{
static void f(void* data)
{
auto f = static_cast<F*>(data);
(*f)(status);
delete f;
}
static void* create(F& f)
{
return new F(std::move(f));
}
};
对于可以放行的F
,我该如何正确实施以下内容?
template<class F>
struct MakeCallback<F, true>
{
static void f(void* data)
{
// from void* to F
}
static void* create(F& f)
{
// form F to void*
}
};
更基本的是,如果我们不将它用作指针,void*
可以保留非地址值吗?它会是UB吗?
答案 0 :(得分:0)
在尝试本答案中显示的代码之前,应该提到一个非常大的警告。这样做很可能是非常未定义的,也不是可移植的行为。我强烈建议不要这样做,因为它可能会很长时间地打破未来,你将很难找到原因。
话虽如此,它似乎至少可以用于我的编译器。其他编译器的结果可能会有所不同。我使用union来转换类实例和void *,不知道有任何其他干净的方法来做到这一点。这应该与sizeof( Class ) <= sizeof( void * )
一样长,但我不保证不同编译器的行为,甚至我在完全相同的设置上使用完全相同的编译器。
#include <iostream>
using namespace std;
class Small {
public:
int i;
};
void *castToVoid( Small& s ) {
union {
Small s;
void *p;
} un;
un.s = s;
return un.p;
}
Small castToSmall( void *p ) {
union {
Small s;
void *p;
} un;
un.p = p;
return un.s;
}
int main( ) {
Small s;
s.i = 100;
void *p = castToVoid( s );
s.i = 200;
cout << p << endl; // Prints 0x64
Small s2 = castToSmall( p );
cout << s2.i << endl; // Prints 100
}
或此示例用于转换为/来自void *
void *castToVoid( Small& s ) {
void **p = reinterpret_cast< void ** >( &s );
return *p;
}
Small castToSmall( void *p ) {
Small *s = reinterpret_cast< Small * >( &p );
return *s;
}
答案 1 :(得分:0)
根据C ++标准(草案N3797),它是实现定义的行为。
§3.7.4.2/p4
脚注38:通过无效指针值间接传递并传递无效 指向释放函数的指针值具有未定义的行为。的不限 其他使用无效指针值具有实现定义 行为强>
某些实现可能会定义复制无效指针 value导致系统生成的运行时错误
§3.7.4.3/p4
实现可能放松了指针安全性,在这种情况下 指针值的有效性不取决于它是否为a 安全派生的指针值。或者,实现可以 有严格的指针安全性,在这种情况下指针值指的是 动态存储持续时间不是安全派生的对象 除非引用,否则指针值是无效的指针值 先前已声明完整对象已被声明可达(20.7.4)[...] 这是实现 定义实现是否已放宽或严格指针安全性。
(强调我的)
因此,如果实现放宽指针安全性是安全的,我们可以使用answer @Smith_61中显示的union
技巧来避免严格的别名。