vladon
vladon

Reputation: 8401

Explicit void pointer as function parameter

I have a function:

int foo(void * ptr)
{
   // ...
}

Can I syntactically (not with compiler warnings, etc.) in C++11/14 disable passing there pointers other than void * itself?

For example, now it can be called like:

foo(new int(42));

I need to disable this.

Upvotes: 18

Views: 2823

Answers (6)

max66
max66

Reputation: 66200

I suppose there are many other ways of doing it.

Using template functions is simple (it works with C++98 too)

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;
 }

As pointed by frymode, the preceding solution give a linker error, not a compiler error, and it's better to get a compiler error.

Using delete (from C++11) we can get a compiler error by using this instead:

template <typename X>
int foo (X ptr) = delete;

Hope this helps.

Upvotes: 20

user541686
user541686

Reputation: 210352

You don't need C++11 to ensure a compile-time error:

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
}

Upvotes: 2

skypjack
skypjack

Reputation: 50540

You can turn the function to a template one, then use a static_assert and std::is_void from type_traits:

template<typename T>
int foo(T *ptr) {
    static_assert(std::is_void<T>::value, "!");
   // ....
}

Otherwise, you can use a std::enable_if_t on the return type:

template<typename T>
std::enable_if_t<std::is_void<T>::value, int>
foo(T *ptr) {
    // ....
    return 0;
}

And so on, other interesting solutions have already been proposed by other users with their answers.

Here is a minimal, working example:

#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
}

Upvotes: 9

dewaffled
dewaffled

Reputation: 2963

If you want exact type match you can use std::enable_if with 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)));
}

Upvotes: 10

marcinj
marcinj

Reputation: 49976

You can make use of pedantic pointer idiom. Your code should look as bellow. It makes use of the fact that there is no implicit conversions at the higher that one level of indirection:

[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**'
}

Upvotes: 11

keith
keith

Reputation: 5332

The idiomatic way would be to create a new type to represent void* to avoid the problem you are describing. Many advocates of good C++ practices suggest creating types to avoid any doubt about what should be passed in and also avoid the compiler letting you.

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)
{
   // ...
}

Upvotes: 6

Related Questions