Явный указатель void как параметр функции
У меня есть функция:
int foo(void * ptr)
{
// ...
}
Можно ли синтаксически (а не с предупреждениями компилятора и т.д.) в С++ 11/14 отключить передачу указателей, отличных от void *
?
Например, теперь его можно вызвать как:
foo(new int(42));
Мне нужно отключить это.
Ответы
Ответ 1
Вы можете превратить функцию в шаблон, затем используйте static_assert
и std::is_void
из type_traits
:
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
}
Ответ 2
Я полагаю, что есть много других способов сделать это.
Использование функций шаблона прост (он также работает с С++ 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;
}
Как указано frymode, предыдущее решение дает ошибку компоновщика, а не ошибку компилятора, и лучше получить ошибку компилятора.
Используя delete
(из С++ 11), мы можем получить ошибку компилятора, используя это вместо:
template <typename X>
int foo (X ptr) = delete;
Надеюсь, что это поможет.
Ответ 3
Если вам нужен точный тип соответствия, вы можете использовать std:: enable_if с станд:: 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)));
}
Ответ 4
Вы можете использовать идиоматический указатель педантичного указателя. Ваш код должен выглядеть ниже. Он использует тот факт, что нет неявных преобразований в более высоком, чем один уровень косвенности:
[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**'
}
Ответ 5
Идиоматическим способом было бы создать новый тип для представления void*
, чтобы избежать проблемы, которую вы описываете. Многие сторонники хорошей практики на С++ предлагают создавать типы, чтобы избежать каких-либо сомнений относительно того, что должно быть передано, а также избегать компилятора, позволяющего вам.
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
Вам не нужно С++ 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
}