Явный указатель 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
}