Конструктор std::thread создает ошибку компилятора MSVC C2661 через конструктор std::tuple

Я следил за выступлением на YouTube Кельвина Хенни, основанного на идиоме функционального С++... Примерно через 50 минут видео он начинает показывать пример структуры класса, которую он назвал channel. Затем он пишет простую функцию fizzbuzz и собирается передать ее серверу как кусок кода, используя потоки. Я использую код из его видео, которое можно найти здесь: Kevlin Henney - Functional C++

Однако, когда я пытаюсь скомпилировать программу, Visual Studio генерирует ошибку компилятора C2661, указывающую на std::tuple..., которая исходит от конструктора std::tread в моем коде.

main.cpp

#include <iostream>
#include <exception>
#include <string>
#include <thread>

#include "server.h"

std::string fizzbuzz(int n) {
    return
        n % 15 == 0 ? "FizzBuzz" :
        n % 3 == 0 ? "Fizz" :
        n % 5 == 0 ? "Buzz" :
        std::to_string(n);
}

void fizzbuzzer(channel<int> & in, channel<std::string> & out) {
    for (;;)
    {
        int n;
        in.receive(n);
        out.send(fizzbuzz(n));
    }
}    

int main() {
    try {
        channel<int> out;
        channel<std::string> back;
        std::thread fizzbuzzing(fizzbuzzer, out, back);
        for (int n = 1; n <= 100; ++n) {
            out << n;
            std::string result;
            back >> result;
            std::cout << result << "\n";
        }    
    }
    catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
        return EXIT_FAILURE;
    }    
    return EXIT_SUCCESS;
}

сервер.h

#pragma once

#include <condition_variable>
#include <queue>
#include <mutex>
#include <iostream>

template<typename ValueType>
class receiving;

template<typename ValueType>
class channel {
private:
    std::mutex key;
    std::condition_variable_any non_empty;
    std::queue<ValueType> fifo;
public:
    void send(const ValueType & to_send) {
        std::lock_guard<std::mutex> guard(key);
        fifo.push(to_send);
        non_empty.notify_all();
    }

    bool try_receive(ValueType & to_receive) {
        bool received = false;
        if (key.try_lock()) {
            std::lock_guard<std::mutex> guard(key, std::adopt_lock);
            if (!fifo.empty()) {
                to_receive = fifo.front();
                fifo.pop();
                received = true;
            }
        }
        return received;
    }

    void receive(ValueType & to_receive) {
        std::lock_guard<std::mutex> guard(key);
        non_empty.wait(
            key,
            [this] {
            return !fifo.empty();
        });
        to_receive = fifo.front();
        fifo.pop();
    }

    void operator<<(const ValueType & to_send) {
        send(to_send);
    }
    receiving<ValueType> operator>>(ValueType & to_receive) {
        return receiving(this, to_receive);
    }    
};

template<typename ValueType>
class receiving {
private:
    channel<ValueType> * that;
    ValueType & to_receive;    
public:
    receiving(channel<ValueType> * that, ValueType & to_receive)
        : that(that), to_receive(to_receive)
    {}
    receiving(receiving && other)
        : that(other.that), to_receive(other.to_receive)
    {
        other.that = nullptr;
    }
    operator bool() {
        auto from = that;
        that = nullptr;
        return from && from->try_recieve(to_receive);
    }
    ~receiving() {
        if (that)
            that->receive(to_receive);
    }
};

Я знаю, что код, который он показывает, является лишь примером кода, но я решил попробовать его в своей IDE, следя за видео, чтобы лучше понять его речь. Я хотел бы иметь возможность скомпилировать это только для того, чтобы увидеть сгенерированный вывод и иметь возможность пройти через отладчик и дизассемблер, но на этом этапе я столкнулся с препятствием. Я понимаю сгенерированную ошибку компилятора, просто не уверен, как ее решить на основе его примера кода...

Вот ошибка компилятора, которая генерируется:

1>------ Build started: Project: Computations, Configuration: Debug Win32 ------
1>main.cpp
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.16.27023\include\memory(2539): error C2661: 'std::tuple<void (__cdecl *)(channel<int> &,channel<std::string> &),channel<int>,channel<std::string>>::tuple': no overloaded function takes 3 arguments
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.16.27023\include\thread(46): note: see reference to function template instantiation 'std::unique_ptr<std::tuple<void (__cdecl *)(channel<int> &,channel<std::string> &),channel<int>,channel<std::string>>,std::default_delete<_Ty>> std::make_unique<std::tuple<void (__cdecl *)(channel<int> &,channel<std::string> &),channel<int>,channel<std::string>>,void(__cdecl &)(channel<int> &,channel<std::string> &),channel<int>&,channel<std::string>&,0>(void (__cdecl &)(channel<int> &,channel<std::string> &),channel<int> &,channel<std::string> &)' being compiled
1>        with
1>        [
1>            _Ty=std::tuple<void (__cdecl *)(channel<int> &,channel<std::string> &),channel<int>,channel<std::string>>
1>        ]
1>c:\users\skilz99\source\repos\computations\computations\main.cpp(31): note: see reference to function template instantiation 'std::thread::thread<void(__cdecl &)(channel<int> &,channel<std::string> &),channel<int>&,channel<std::string>&,void>(_Fn,channel<int> &,channel<std::string> &)' being compiled
1>        with
1>        [
1>            _Fn=void (__cdecl &)(channel<int> &,channel<std::string> &)
1>        ]
1>Done building project "Computations.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

person Francis Cugler    schedule 28.06.2020    source источник
comment
Кроме того, если я изменю его на сборку для x64, он все равно выдаст ту же ошибку компилятора...   -  person Francis Cugler    schedule 28.06.2020
comment
Я думаю, что это будет некопируемое mutex, но это дерьмовое сообщение об ошибке.   -  person user4581301    schedule 28.06.2020
comment
Неа. Я не прав.   -  person user4581301    schedule 28.06.2020
comment
@user4581301 user4581301 Возможно, но я также вижу, где std::unique_ptr тоже появляется в сгенерированной ошибке компилятора ... и std::unique_ptr не подлежит копированию, если я правильно помню, в отличие от std::shared_ptr ... но это не должно иметь ничего общего с не разрешая 3 аргумента... тогда опять же это может...   -  person Francis Cugler    schedule 28.06.2020
comment
Думал, у меня пример попроще, но оказалось, что я отслеживал что-то другое   -  person user4581301    schedule 28.06.2020
comment
thread принимают параметры по значению, и вы не можете скопировать mutex или condition_variable, так что это ошибка, но я не понимаю, как это влияет на ошибку, которую вы видите.   -  person user4581301    schedule 28.06.2020
comment
Упрощенный пример: godbolt.org/z/bZJzJq, и ошибка исчезнет, ​​если вы сделаете channel копируемым или пройдете это по ссылке, но это одно серьезно непостижимое сообщение об ошибке.   -  person user4581301    schedule 28.06.2020
comment
@ StoryTeller-UnslanderMonica Хорошо, использование оболочки std::ref избавило от ошибки компилятора. Я могу скомпилировать и запустить программу... Однако она не свободна от ошибок после того, как 99, Fizz, Buzz напечатаны, отладчик вызывает ошибку отладки, указывающую, что abort() был вызван, и приложение завершает работу с кодом ошибки 3... Но, как я уже сказал, это всего лишь sample код, так что ничего страшного!   -  person Francis Cugler    schedule 28.06.2020
comment
Это просто хорошая практика, чтобы лучше понять различные идиомы в программировании в целом, а не только в C++. Это была интересная презентация. Концепция функционального программирования в отличие от других идиом и парадигм... {объектно-ориентированных, процедурных, универсальных, модульных, наблюдательных шаблонов и т. д.} в C++ была информативной. Это определенно не производственный код, но, возможно, я смогу убрать некоторые концепции, которые были представлены в этом видео... они могут быть полезны в определенных контекстах использования.   -  person Francis Cugler    schedule 28.06.2020
comment
Вам нужно присоединиться к потоку, прежде чем он выйдет за рамки, это может исправить прерывание   -  person Alan Birtles    schedule 28.06.2020
comment
@AlanBirtles Хорошо, это имеет смысл, и, поскольку я не очень хорошо разбираюсь в использовании потоков ... могу ли я присоединиться к нему до или после цикла for?   -  person Francis Cugler    schedule 28.06.2020
comment
Предположительно после того, как вы хотите дождаться завершения выполнения потока   -  person Alan Birtles    schedule 28.06.2020
comment
@AlanBirtles Хорошо, я так и думал, но хотел убедиться!   -  person Francis Cugler    schedule 29.06.2020
comment
Ваша проблема решена? Если нет, я думаю, что эта ссылка может вам помочь.   -  person Barrnet Chou    schedule 29.06.2020