Ошибка компиляции, когда поток вызывает шаблон функции (для аргумента по умолчанию)

SSteven спросил: 28 марта 2018 в 02:57 в: c++

Следующий шаблон функции из книги Страустрапа ("CPL", 4th Ed):

template<typename Arg1, typename Arg2, typename Arg3>
void write(Arg1 a1, Arg2 a2 = {}, Arg3 a3 = {});template<typename Arg1, typename Arg2, typename Arg3>
void write(Arg1 a1, Arg2 a2, Arg3 a3)
{
   thread::id name = this_thread::get_id();   coutm.lock();   cout << "From thread " << name << " : "
        << a1 << ' ' << a2 << ' ' << a3
        << endl;   coutm.unlock();
}


Я пытаюсь сделать следующее здесь:
1 ) Вызовите эту функцию, передав всего 2 аргумента. Третий аргумент должен быть установлен по умолчанию.
2) Вызов шаблона функции передается непосредственно в поток (а не через лямбда).

int main()
{
   thread t (write<char, float>, 'a', 4.9);   t.join();
}


Однако, я получаю следующую ошибку компиляции:

main.cpp: In function 'int main()':
main.cpp:22:42: error: no matching function for call to 'std::thread::thread(<unresolved overloaded function type>, char, double)'    thread t (write<char, float>, 'a', 4.9);
                                          ^
In file included from main.cpp:7:0:
/usr/local/include/c++/7.2.0/thread:118:7: note: candidate: template<class _Callable, class ... _Args> std::thread::thread(_Callable&&, _Args&& ...)       thread(_Callable&& __f, _Args&&... __args)
       ^~~~~~
/usr/local/include/c++/7.2.0/thread:118:7: note:   template argument deduction/substitution failed:
main.cpp:22:42: note:   couldn't deduce template parameter '_Callable'    thread t (write<char, float>, 'a', 4.9);
                                          ^
In file included from main.cpp:7:0:
/usr/local/include/c++/7.2.0/thread:113:5: note: candidate: std::thread::thread(std::thread&&)     thread(thread&& __t) noexcept
     ^~~~~~/usr/local/include/c++/7.2.0/thread:113:5: note:   candidate expects 1 argument, 3 provided
/usr/local/include/c++/7.2.0/thread:106:5: note: candidate: std::thread::thread()     thread() noexcept = default;
     ^~~~~~/usr/local/include/c++/7.2.0/thread:106:5: note:   candidate expects 0 arguments, 3 provided


Можно ли вызвать такой шаблон функции напрямую через поток, для аргумента по умолчанию? Если да, то как? Или иначе, нужно ли лямбда передать конструктору потока?

3 ответа

Davis Herring ответил: 28 марта 2018 в 09:34

Аргумент шаблона не может быть выведен из аргумента по умолчанию (функции). Вы не указали Arg3 явно, поэтому нет способа создать экземпляр write. Иными словами, какой тип вы ожидаете, что a3 будет в вызове потока write?

SSteven ответил: 29 марта 2018 в 04:33
Хороший вопрос, но исправление Ripi2 - которое использует тип по умолчанию - должно было сработать. Но это не так.
Davis Herring ответил: 06 апреля 2018 в 11:35
@SSteven: я думаю, что здесь могут быть ошибки реализации: я ничего не вижу в thread.thread.constr или [func.require], чтобы предположить, что аргумент рассчитывает и печатает (здесь аргумент double для float должен соответствовать больше , чем для обычного вызова.
SSteven ответил: 29 марта 2018 в 04:32
Если вы не передаете шаблон функции записи в поток, нет необходимости явно указывать аргументы шаблона. Типы будут выведены: write('a', 4.9); будет работать.
Ripi2 ответил: 29 марта 2018 в 05:56
template<typename Arg1, typename Arg2, typename Arg3>
void write(Arg1 a1, Arg2 a2 = {}, Arg3 a3 = {});
......
thread t (write<char, float>, 'a', 4.9);

Вызовите эту функцию, передав только 2 аргумента. Третий аргумент должен использоваться по умолчанию.

Компилятор будет использовать аргумент по умолчанию, если он знает его тип, что не соответствует вашему случаю.


Первый исправить - дать тип аргумента по умолчанию для объявления write:

template<typename Arg1, typename Arg2, typename Arg3 = int> //default Arg3 type
void write(Arg1 a1, Arg2 a2 = {}, Arg3 a3 = {});

Это все равно дает ошибки, хорошо новые ошибки. Я тестирую с g ++ 7.3, clang 6.0.0 и icc 18.0.0 из Compiler Explorer

Интересно, что g ++ допускает тип по умолчанию typename Arg3 = int или другой тип из объявления, также в определение (в следующей попытке, см. ниже), но не только в определении. Clang и icc терпят неудачу в любом случае, кроме только логического "в объявлении".

Попробуйте исправить, но без потока:

int main()
{
   //thread t (write<char, float>, 'a', 4.9);
   write<char, float>('a', 4.9);   //t.join();
}

Ах, ха , Теперь он компилируется с g ++ 7.3, clang 6.0.0 и icc 18.0.0 и выводит a 4.9 0


Так что проблема в конструкторе std :: thread Почему?

icc-компилятор ожидает того же числа аргументов функций, что и количество этих аргументов после вывода шаблона, несмотря на то, имеют ли они значения по умолчанию (т. е. = {}) или нет:

error: static assertion failed with "Wrong number of arguments for function"
        static_assert(sizeof...(_BoundArgs) == sizeof...(_Args),
        ^          detected during:
            instantiation of class "std::_Bind_check_arity<_Ret (*)(_Args...), _BoundArgs...> [with _Ret=void, _Args=<char, float, int>, _BoundArgs=<char, double>]"  
            instantiation of class "std::_Bind_simple_helper<_Func, _BoundArgs...> [with _Func=void (&)(char, float, int), _BoundArgs=<char, double>]"
            instantiation of "std::thread::thread(_Callable &&, _Args &&...) [with _Callable=void (&)(char, float, int), _Args=<char, double>]" 

ошибка лягушки:

 error: no matching member function for call to '_M_invoke'
        -> decltype(std::declval<_Invoker&>()._M_invoke(_Indices()))
                    ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~ note: in instantiation of template class 'std::thread::_Invoker<std::tuple<void (*)(char, float, int), char, double> >' requested here
         __make_invoker(std::forward<_Callable>(__f),

ошибка gcc:

In instantiation of 'struct std::thread::_Invoker<std::tuple<void (*)(char, float, int), char, double> >':
required from 'std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(char, float, int); _Args = {char, double}]'
required from here
error: no matching function for call to 'std::thread::_Invoker<std::tuple<void (*)(char, float, int), char, double> >::_M_invoke(std::thread::_Invoker<std::tuple<void (*)(char, float, int), char, double> >::_Indices)'
  operator()()
  ^~~~~~~~