【问题标题】:Passing a void* by reference通过引用传递 void*
【发布时间】:2011-06-28 00:27:50
【问题描述】:

为什么我不能通过引用传递void*?编译器允许我声明一个具有以下签名的函数:

static inline void FreeAndNull(void*& item)

但是当我尝试调用它时,我收到以下错误:

Error   1   error C2664: 'FreeAndNull' : cannot convert parameter 1 from 'uint8_t *' to 'void *&'

将其转换为 void* 也不起作用

另外,有什么解决方法吗?

【问题讨论】:

  • 你的代码中存在这个函数意味着你的代码可以改进。你不应该手动删除东西,也不应该关心指针的值是什么。请改用智能指针。
  • @GMan:很好的建议,但是为了这个问题的目的,情况被简化了

标签: c++ function pointers reference void-pointers


【解决方案1】:

答案是肯定的,您可以通过引用传递void*,而您得到的错误与此无关。问题是,如果您有一个通过引用获取void* 的函数,那么您只能将实际上是void*s 的变量作为参数传递。这是有充分理由的。例如,假设你有这个函数:

void MyFunction(void*& ptr) {
    ptr = malloc(137); // Get a block of raw memory
}

int* myIntArray;
MyFunction(myIntArray); // Error- this isn't legal!

由于指定的调用,上面的代码是非法的,并且有充分的理由。如果我们可以将myIntArray 传入MyFunction,它将被重新分配为指向void* 类型的缓冲区,该缓冲区不是int 数组。因此,从函数返回时,您的int* 将指向void* 类型的数组,从而颠覆了类型系统。这并不是说 C++ 有一个强大的类型系统——它没有——但如果你要颠覆它,你必须显式地进行一些强制转换。

你同样不能这样做:

void MyFunction(void*& ptr) {
    ptr = malloc(137); // Get a block of raw memory
}

int* myIntArray;
MyFunction((void*)myIntArray); // Error- this isn't legal!

作为一个很好的参考,你为什么不能这样做,想想这段代码:

void OtherFunction(int& myInt) {
    myInt = 137;
}

double myDouble;
OtherFunction((int)myDouble); // Also error!

这是不合法的,因为如果您尝试将 double 传递给采用 int& 的函数,那么由于 intdouble 具有根本不同的二进制表示,您最终会破坏double 的位与无意义的数据。如果这个演员阵容是合法的,就有可能做这样的坏事。

简而言之,是的,您可以输入void* &,但如果您这样做,则必须输入真实的void*s。正如 Erik 上面所指出的,如果您想释放指针并将指针模板归零,那么您可以这样做。

如果你真的想将uint_8* 传递给你的函数,你可以这样做:

uint_8* uPtr = /* ... */
void* ptr = uPtr;
FreeAndNull(ptr);
uPtr = (uint_8*) ptr;

这需要显式转换来告诉编译器“是的,我知道这可能不安全,但无论如何我都会这样做。”

希望这会有所帮助!

【讨论】:

  • +1,现在这很有意义。左值规则和右值流口水!
  • @Cameron, templatetypedef: 顺便说一句,用(void*&) 投射它会“工作”(因为它会编译,我不确定以后使用它是否是UB)
  • @Crazy Eddie- (我假设你对此投了反对票;如果你没有,请忽略这个)你能解释一下这个答案有什么问题吗?我希望这很有用,所以我能做的任何事情都将不胜感激。
  • 查看我的回答和我对您的评论的评论。
【解决方案2】:

如果通过引用获取 void *,则必须传递实际的 void *,而不是 uint8_t *。

试试这个:

template<typename T> inline void FreeAndNull(T * & V) { free(V); V = 0; }

编辑:修改示例以更好地反映 OP 的函数名称,并解决 @6502 完全正确的评论。

【讨论】:

  • +1,酷!但现在我很好奇:为什么演员表在 OP 的示例中不起作用?
  • 这在我看来是一个冒险的实现。我想 OP 没有分配单个 uint8_t 元素,所以使用 delete 而不是 delete[] 是未定义的行为。
  • @Cameron- 请参阅下面我的回答,了解为什么演员表不起作用。技术版本是它在需要左值的地方产生一个右值,但直观的原因是它不安全。
  • @template - 没错,但你的回答甚至没有提到左值/右值。
【解决方案3】:

转换为 void* 不起作用的原因比其他地方解释的要简单得多:除非您明确指定引用,否则转换会创建右值。您不能将右值作为非常量引用传递。

证明?试试这个:

void fun(void * &) {}
int main() { int * x; void * x2 = x; fun(x); }

如果它适合你使用,试试这个:

void fun(void * const&);

现在不仅可以进行强制转换,还可以使用隐式。

强制转换为引用可能很危险。它确实会导致您的代码编译,但我不会谈论它的行为:

void fun(void *&){}
int main() { int * x; fun((void*&)x); }

我敢打赌,这会做非常糟糕的事情(tm),因为您实际上是在这里进行 reinterpret_cast 而不是静态转换。

【讨论】:

  • “强制转换创建右值”不一定正确。 (void*&amp;)x 产生一个左值。
  • 看起来像 const-ref 一样传递绝对不是 OP 想要的。
  • @Fred - 因为外表可能具有欺骗性,我不明白你的意思。
猜你喜欢
  • 1970-01-01
  • 2011-02-08
  • 1970-01-01
  • 2015-01-12
  • 2013-09-10
  • 2020-12-29
  • 2020-04-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多