Reputation: 8401
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
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
Reputation: 210352
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
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
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
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:
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
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