显式 void 指针作为函数参数
Posted
技术标签:
【中文标题】显式 void 指针作为函数参数【英文标题】:Explicit void pointer as function parameter 【发布时间】:2016-12-13 07:54:51 【问题描述】:我有一个函数:
int foo(void * ptr)
// ...
我可以在 C++11/14 中在语法上(不使用编译器警告等)禁用传递 void *
本身以外的指针吗?
例如,现在可以这样调用:
foo(new int(42));
我需要禁用它。
【问题讨论】:
这样做的目的是什么?什么会阻止某人做例如void* ptr = reinterpret_cast<void*>(new int(42)); foo(ptr);
?您能否详细说明用例以及您要解决的实际问题?
我需要一个“显式”的 void 参数,就像在类构造函数中一样。如果用户明确重新解释它,这是他的权利。实际问题:想象一下智能指针,std::unique_ptr
的一些类似物,它允许 void*
保持,但仅适用于传递给构造函数的 void *
参数。
那如何使用模板和type-traits?
@JoachimPileborg 可能是一个变种,你能帮我吗:如何禁用模板类的方法,并根据模板参数禁用?
@JoachimPileborg:这是一个非常愚蠢的论点。这就像说禁止intptr_t
也没有任何意义,因为任何人都可以将reinterpret_cast
发送到void*
并返回。类型检查是安全功能,而不是安全功能。
【参考方案1】:
我想还有很多其他方法可以做到这一点。
使用模板函数很简单(它也适用于 C++98)
template <typename X>
int foo (X * ptr);
int foo (void * ptr)
return 1;
int main()
int i;
void * vp = &i;
foo(vp); // OK
foo(&i); // linker error
return 0;
正如friedmode所指出的,前面的解决方案给出的是链接器错误,而不是编译器错误,最好是编译器错误。
使用 delete
(来自 C++11)我们可以通过使用它来获得编译器错误:
template <typename X>
int foo (X ptr) = delete;
希望这会有所帮助。
【讨论】:
我喜欢有人为了他们的利益而改变决议规则:) 这会导致链接器错误而不是编译器,这使得查找错误源变得更加烦人。 @frymode - 你是对的;谢谢;用另一个给出编译器错误的示例修改了我的答案(但仅从 C++11 开始)【参考方案2】:您可以使用pedantic pointer idiom。您的代码应如下所示。它利用了在更高一级间接没有隐式转换的事实:
[live]
int foo_impl(void * ptr, void **)
return 0;
template <typename T>
void foo(T* t)
foo_impl(t, &t);
int main()
void* pv;
foo(pv);
//foo(new int(2)); // error: error: invalid conversion from 'int**' to 'void**'
【讨论】:
好招!但如果它在某些情况下与优化混淆,我不会感到惊讶。 @Mehrdadvoid foo_impl(void *ptr)
可以使用;通话有很多选择,例如static_cast<void **>(&t), foo_impl(t);
。编译器应该能够应付这个【参考方案3】:
如果你想要精确的类型匹配,你可以使用std::enable_if 和std::is_same
#include <iostream>
#include <type_traits>
template <typename T,
typename = typename std::enable_if_t<std::is_same<T, void*>::value>>
int foo(T value)
return 5;
int main()
// return foo(new int(42)); // error: no matching function for call to 'foo(int*)'
return foo((void*)(new int(42)));
【讨论】:
这个守卫很弱。请参阅here,使用foo<int*, void>(new int(42));
可以轻松解决它。
改用template <typename T, typename..., typename = typename std::enable_if_t<std::is_same<T, void*>::value>>
就不行了。【参考方案4】:
您可以将函数转换为模板函数,然后使用来自type_traits
的static_assert
和std::is_void
:
template<typename T>
int foo(T *ptr)
static_assert(std::is_void<T>::value, "!");
// ....
否则,您可以在返回类型上使用std::enable_if_t
:
template<typename T>
std::enable_if_t<std::is_void<T>::value, int>
foo(T *ptr)
// ....
return 0;
等等,其他用户已经提出了其他有趣的解决方案并给出了答案。
这是一个最小的工作示例:
#include<type_traits>
template<typename T>
int foo(T *ptr)
static_assert(std::is_void<T>::value, "!");
// ....
return 0;
int main()
int i = 42;
void *p = &i;
foo(p);
// foo(&i); // compile error
【讨论】:
【参考方案5】:惯用的方法是创建一个新类型来表示 void*
以避免您描述的问题。许多良好 C++ 实践的倡导者建议创建类型以避免对应该传入的内容产生任何疑问,同时也避免编译器让您失望。
class MyVoid
//... implement in a way that makes your life easy to do whatever you are trying to do with your void* stuff
;
int foo(MyVoid ptr)
// ...
【讨论】:
【参考方案6】:您不需要 C++11 来确保编译时错误:
template<class> struct check_void;
template<> struct check_void<void> typedef void type; ;
template<class T> typename check_void<T>::type *foo(T *ptr) return ptr;
int main()
foo(static_cast<void *>(0)); // success
foo(static_cast<int *>(0)); // failure
【讨论】:
以上是关于显式 void 指针作为函数参数的主要内容,如果未能解决你的问题,请参考以下文章
C++ CreateThread函数如何传递this指针作为参数
如何检查 googlemock 中作为 void 指针传递的字符串参数
C 语言数据类型本质 ( void 关键字作用 | 数据类型封装 | 作为 参数 或 返回值 代表无 | void* 指针赋值与被赋值 | void 类型变量不存在 )