Почему NULL не может быть аргументом для параметра void * функции выполнения std :: thread? - PullRequest
1 голос
/ 25 марта 2020

Вот минимальный случай:

#include <iostream>
#include <functional>
#include <thread>
void fun(void *args) {}
int main() {
  std::thread thread_id(fun, NULL);
}

g ++ Компиляция не удалась:

[firstlove@manjaro misc]$ g++ thread_and_shared_resources.cpp -lpthread -std=c++17
In file included from thread_and_shared_resources.cpp:3:
/usr/include/c++/9.3.0/thread: In instantiation of 'std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(void*); _Args = {long int}; <template-parameter-1-3> = void]':
thread_and_shared_resources.cpp:6:34:   required from here
/usr/include/c++/9.3.0/thread:120:44: error: static assertion failed: std::thread arguments must be invocable after conversion to rvalues
  120 |           typename decay<_Args>::type...>::value,
      |                                            ^~~~~
/usr/include/c++/9.3.0/thread: In instantiation of 'struct std::thread::_Invoker<std::tuple<void (*)(void*), long int> >':
/usr/include/c++/9.3.0/thread:131:22:   required from 'std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(void*); _Args = {long int}; <template-parameter-1-3> = void]'
thread_and_shared_resources.cpp:6:34:   required from here
/usr/include/c++/9.3.0/thread:243:4: error: no type named 'type' in 'struct std::thread::_Invoker<std::tuple<void (*)(void*), long int> >::__result<std::tuple<void (*)(void*), long int> >'
  243 |    _M_invoke(_Index_tuple<_Ind...>)
      |    ^~~~~~~~~
/usr/include/c++/9.3.0/thread:247:2: error: no type named 'type' in 'struct std::thread::_Invoker<std::tuple<void (*)(void*), long int> >::__result<std::tuple<void (*)(void*), long int> >'
  247 |  operator()()
      |  ^~~~~~~~
[firstlove@manjaro misc]$ g++ thread_and_shared_resources.cpp -lpthread -std=c++17
In file included from thread_and_shared_resources.cpp:3:
/usr/include/c++/9.3.0/thread: In instantiation of 'std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(void*); _Args = {long int}; <template-parameter-1-3> = void]':
thread_and_shared_resources.cpp:6:34:   required from here
/usr/include/c++/9.3.0/thread:120:44: error: static assertion failed: std::thread arguments must be invocable after conversion to rvalues
  120 |           typename decay<_Args>::type...>::value,
      |                                            ^~~~~
/usr/include/c++/9.3.0/thread: In instantiation of 'struct std::thread::_Invoker<std::tuple<void (*)(void*), long int> >':
/usr/include/c++/9.3.0/thread:131:22:   required from 'std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(void*); _Args = {long int}; <template-parameter-1-3> = void]'
thread_and_shared_resources.cpp:6:34:   required from here
/usr/include/c++/9.3.0/thread:243:4: error: no type named 'type' in 'struct std::thread::_Invoker<std::tuple<void (*)(void*), long int> >::__result<std::tuple<void (*)(void*), long int> >'
  243 |    _M_invoke(_Index_tuple<_Ind...>)
      |    ^~~~~~~~~
/usr/include/c++/9.3.0/thread:247:2: error: no type named 'type' in 'struct std::thread::_Invoker<std::tuple<void (*)(void*), long int> >::__result<std::tuple<void (*)(void*), long int> >'
  247 |  operator()()
      |  ^~~~~~~~

Почему NULL здесь нельзя передать? автономный вызов fun с NULL работает просто отлично:

fun(NULL) // compiles happily

Ответы [ 2 ]

2 голосов
/ 25 марта 2020

NULL расширяется до 0 в вашей реализации, а 0 является целым числом, а не указателем.
В некоторых контекстах он может быть преобразован в указатель, но не в других; это пример последнего.

Используйте nullptr, это решает много подобных проблем.

1 голос
/ 26 марта 2020

TL; DR: NULL является устаревшим C ++. В современном C ++ используйте nullptr в качестве прямой замены для NULL, чтобы избежать подобных проблем.

Q: Почему NULL здесь нельзя передать ? автономный вызов fun с NULL прекрасно работает

1) В glib c, NULL в режиме C ++ # определяется как 0L 1

2) Конструктор std::thread использует std::invoke to forward его аргументов в вызываемом аргументе. При пересылке значения r * 0L оно в первую очередь становится переменной (ссылочного типа).

3) Правило неявного преобразования нулевого указателя в C ++ указывается в [conv.ptr] / 1 :

A константа нулевого указателя является целочисленным литералом со значением ноль или значением типа std​::​nullptr_­t. Константа нулевого указателя может быть преобразована в тип указателя; результат - значение нулевого указателя этого типа и отличается от любого другого значения указателя объекта или типа указателя функции. Такое преобразование называется преобразованием нулевого указателя .

Это означает, что C ++ допускает целочисленных литералов со значением 0 (таким образом, буквально 0, 0L, 0u et c.), Но не другие целые значения , для неявного преобразования в тип указателя:

void fun(void* args) {}

int main() {

    fun(0); // OK

    int arg = 0;
    fun(arg); // error: invalid conversion from 'int' to 'void*'

}

Теперь, поскольку std::invoke является шаблон, root вызывает ошибку, " недопустимое преобразование из 'long int' в 'void ' *" в вашем случае подпадает под правила SFINAE и молча проглатывается, оставляя вас с неясной ошибкой о проблеме с аргументами std::thread s.

Обходной путь должен использовать nullptr вместо NULL.


1 в режиме C NULL is (void*)0, но это запрещено в C ++.

...