18

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.

max66
  • 65,235
  • 10
  • 71
  • 111
vladon
  • 8,158
  • 2
  • 47
  • 91
  • 7
    And what would be the purpose of that? What would stop someone from doing e.g. `void* ptr = reinterpret_cast(new int(42)); foo(ptr);`? Can you please elaborate on the use-case, on the actual problem you are trying to solve? – Some programmer dude Aug 07 '16 at 12:11
  • I need an "explicit" void parameter, like in class constructors. If a user reinterpreting it **explicitly**, it's his right. Actual problem: imagine smart pointer, some analogue of `std::unique_ptr`, which allows `void*` to hold, but only for `void *` params passed to constructor. – vladon Aug 07 '16 at 12:13
  • Then how about using templates and [type-traits](http://en.cppreference.com/w/cpp/types#Type_traits_.28since_C.2B.2B11.29)? – Some programmer dude Aug 07 '16 at 12:15
  • @JoachimPileborg May be it's a variant, can you help me: how method of templated class may be disabled, and disabling based on template parameters? – vladon Aug 07 '16 at 12:17
  • @JoachimPileborg: That's a really silly argument. It's like saying there's no point in disallowing `intptr_t` either, because anyone could just `reinterpret_cast` it to `void*` and back. Type checks are safety features, not security features. – user541686 Aug 08 '16 at 09:26
  • @Mehrdad Compile-time checks is not to prevent special hacks, but to avoid Murphy's law :-) I.e. to avoid accidental errors. If you specially write `reinterpret_cast` or smth like it, than you understand what you doing. – vladon Aug 08 '16 at 09:41
  • vladon: I think you're telling me the same thing I was trying to tell @Joachim... – user541686 Aug 08 '16 at 09:45
  • @vladon: No worries. Also, don't forget to take a look at my answer below. You don't need C++11 to get compile-time checking (despite claims to the contrary in the top answer). – user541686 Aug 09 '16 at 18:48
  • A function that takes `void*` and nothing else is very unlikely to be able to do something meaningful, because it doesn't even know the size of the data to work with. – alfC Apr 26 '20 at 03:02
  • @alfC the snow is white, and the rain is wet, and 2+2=4. Thank you. – vladon Apr 28 '20 at 07:59
  • @vladon, thank you for the poem, the point is that forcing a pointer argument to be exactly `void*` is a code smell. – alfC Apr 28 '20 at 08:41

6 Answers6

20

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.

max66
  • 65,235
  • 10
  • 71
  • 111
11

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**'
}
marcinj
  • 48,511
  • 9
  • 79
  • 100
  • Nice trick! But I wouldn't be surprised if it messed with optimizations in some cases. – user541686 Aug 09 '16 at 18:50
  • @Mehrdad `void foo_impl(void *ptr)` could be used; the call has many options e.g. `static_cast(&t), foo_impl(t);`. Compiler should be able to cope with that – M.M Aug 23 '16 at 01:30
10

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)));
}
skypjack
  • 49,335
  • 19
  • 95
  • 187
dewaffled
  • 2,850
  • 2
  • 17
  • 30
9

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
}
skypjack
  • 49,335
  • 19
  • 95
  • 187
6

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)
{
   // ...
}
keith
  • 5,122
  • 3
  • 21
  • 50
2

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
}
Community
  • 1
  • 1
user541686
  • 205,094
  • 128
  • 528
  • 886