在 C++ 中,不满足要求并不一定意味着你失败了

Exchange insights, tools, and strategies for canada dataset.
Post Reply
jrineakter
Posts: 859
Joined: Thu Jan 02, 2025 7:15 am

在 C++ 中,不满足要求并不一定意味着你失败了

Post by jrineakter »

我被要求帮助调试一个问题,即传递给函数的指针与原始值略有偏差。该指针作为不透明参数传递,应该从管道的另一端输出。但不知何故,从另一端输出的值略有不同。

为了说明的目的,我们假设我们尝试调用的底层函数是这个。

void SubmitWork(HWORKER worker, int commandCode, void* param);
您Submit­Work使用一个工作者、一个命令代码以及一个指向依赖于命令代码的一些数据的原始指针进行调用。

开发人员向我发送了一些调试输出,显示处理命令代码的函数收到的指针与传递的指针不同。一种可能性是库中有一个错误,导致它无意中修改了最终的指针参数,但这似乎 所有者/合伙人/股东电子邮件列表 不太可能,因为该值对库来说应该是不透明的。我能够与开发人员进行视频通话,观看他们逐步完成代码,在那里我注意到了一些有趣的事情。

开发人员的代码实际上并没有调用Submit­Work,而是使用了一个包装器,进而调用Submit­Work:


包装器试图Submit­Work()通过让您传递标准布局类型作为第二个参数来使该函数更易于使用,并且它将通过地址传递它。这个想法是,除了传递原始指针之外,您还可以传递如下结构:


::提交工作(m_hWorker,CMD_SETSIZE,&info);
经过额外检查,info发现其为标准布局类型(可以是memcpy“d”)。

但事实证明,这个帮手并没有那么大的帮助。

假设有人以老式的指针方式调用该方法:

;
由于指针是标准布局类型,因此第二次过载正在发挥作用。

而第二次超载获胜,因为比char*&更匹配!char*&void*

::Submit­Work()这意味着模板辅助函数不是将指针传递给,而是将指针的地址::Submit­Work()传递给。

这就是在处理工作时指针错误到达的原因:传递给处理器的指针::Submit­Work被正确传递。问题是::Submit­Work一开始就传递了错误的指针!

解决此问题的一种方法是使指针不适合模板函数。


在这里,我将一个指针传递给非标准布局类型,并且由于模板函数不适合指针,所以唯一剩下的过载是采用的过载void*,它std::string*可以顺利转换为。

助手们试图确保只将指向标准布局类型的指针传递给::Submit­Work,但我能够潜入其中,std::string*因为::Submit­Work执行强制执行的模板函数将自己从考虑中移除,而不是接受参数然后抱怨它。

在 C++ 中,如果编译器无法满足某个函数的约束,那么该函数将被忽略,并且不会考虑进行重载解析。您可能知道:假设您有两个函数重载do_something():

无效做某事(char* p);
无效做某事(int v);
当你写 时do_something(42),第一个重载会被忽略,因为42无法转换为char*,而第二个重载会被使用,因为42确实可以转换为int。你没想到重载会出错char*。

但在我们的例子中Submit­Work(),如果您使用指向非标准布局类型的指针来调用它,我们不希望忽略模板版本。我们希望它触发错误!但它没有,因为这是一个失败的替换,众所周知,替换失败不是错误 (SFINAE)。我们想要做的是允许替换成功,但如果类型不是标准布局,则在函数体中生成错误。


雷蒙德参与 Windows 的开发已有 30 多年。2003 年,他创办了一个名为 The Old New Thing 的网站,该网站的受欢迎程度远远超出了他的想象,这一发展至今仍让他感到毛骨悚然。该网站催生了一本书,巧合的是,这本书也名为 The Old New Thing(Addison Wesley 2007)。他偶尔会出现在 Windows Dev Docs Twitter 帐户上,讲述一些毫无用处的故事。
Post Reply