显式 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**'

【讨论】:

好招!但如果它在某些情况下与优化混淆,我不会感到惊讶。 @Mehrdad void foo_impl(void *ptr) 可以使用;通话有很多选择,例如static_cast&lt;void **&gt;(&amp;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&lt;int*, void&gt;(new int(42)); 可以轻松解决它。 改用template &lt;typename T, typename..., typename = typename std::enable_if_t&lt;std::is_same&lt;T, void*&gt;::value&gt;&gt; 就不行了。【参考方案4】:

您可以将函数转换为模板函数,然后使用来自type_traitsstatic_assertstd::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 类型变量不存在 )

将“void”作为函数参数传递

函数指针作为类方法声明中的参数

指针变量作为函数的参数