C++ 在具有可调用对象的线程中生成线程

C++ 在具有可调用对象的线程中生成线程,c++,multithreading,callable,callable-object,C++,Multithreading,Callable,Callable Object,我已经在很多场合看到过这个问题,它似乎出现在Windoes(visual studio)和Linux(gcc)中。 以下是它的简化版本: class noncopyable { public: noncopyable(int n); ~noncopyable(); noncopyable(const noncopyable&) = delete; noncopyable& operator=(const noncopyable&) = de

我已经在很多场合看到过这个问题,它似乎出现在Windoes(visual studio)和Linux(gcc)中。 以下是它的简化版本:

class noncopyable
{
public:
    noncopyable(int n);
    ~noncopyable();
    noncopyable(const noncopyable&) = delete;
    noncopyable& operator=(const noncopyable&) = delete;
    noncopyable(noncopyable&&);
    int& setvalue();
private:
    int* number;
};

class thread_starter
{
public:
    template<typename callable>
    bool start(callable & o);
};

template<typename callable>
inline bool thread_starter::start(callable & o)
{
    std::thread t(
        [&]() {
        int i = 10;
        while (i-- > 0)
        {
            noncopyable m(i);
            std::thread child(o, std::move(m));
            child.detach();
        }
    });
    return true;
}

class callable
{
public:
    virtual void operator()(noncopyable &m);
};

void callable::operator()(noncopyable & m) { m.setvalue()++; }


int main()
{
    thread_starter ts;
    callable o;
    ts.start(o);
}
类不可复制
{
公众:
不可复制(int-n);
~noncopyable();
不可复制(const noncopyable&)=删除;
noncopyable&运算符=(const noncopyable&)=delete;
不可复制(不可复制&&);
int&setvalue();
私人:
整数*数字;
};
类螺纹起动器
{
公众:
模板
bool启动(可调用&o);
};
模板
内联bool线程启动程序::启动(可调用&o)
{
螺纹t(
[&]() {
int i=10;
而(i-->0)
{
不可复制m(i);
std::线程子级(o,std::move(m));
child.detach();
}
});
返回true;
}
类可调用
{
公众:
虚拟void操作符()(不可复制&m);
};
void callable::operator()(noncopyable&m){m.setvalue()++;}
int main()
{
螺纹起动器;
可调用o;
ts.start(o);
}
代码似乎足够合法,但无法编译

在Visual Studio中,它将提供:

error C2893: Failed to specialize function template 'unknown-type std::invoke(_Callable &&,_Types &&...) noexcept(<expr>)'
error: no type named ‘type’ in ‘class std::result_of<callable(int)>’....
错误C2893:未能专门化函数模板“未知类型std::invoke(_Callable&,_Types&&…)noexcept()”
在GCC中,它将给出:

error C2893: Failed to specialize function template 'unknown-type std::invoke(_Callable &&,_Types &&...) noexcept(<expr>)'
error: no type named ‘type’ in ‘class std::result_of<callable(int)>’....
错误:在“…”的“class std::result_”中没有名为“type”的类型。。。。
我想我知道问题在于某种形式的复制或引用机制,但所有语法似乎都是正确的

我错过了什么


我对这个例子做了一点修改,对于造成的混乱我深表歉意。我正试图尽可能纯粹地重现这个问题,但我自己并不完全理解。

调用
std::thread
会创建一个元组。无法使用引用初始化元组。因此,您必须使用一个伪引用,
std::ref(i)
来编译它,并使用int-refs
int&
调用可调用项

template <typename callable>
bool thread_starter::start(callable &o)
{
    // Nonsense
    std::thread t(
        [&]() {
        int i = 10;
        while (i-- > 0)
        {
            std::thread child(o, std::ref(i));
            child.detach();
        }
    });
    return true;
}
模板
bool线程启动程序::启动(可调用&o)
{
//胡说八道
螺纹t(
[&]() {
int i=10;
而(i-->0)
{
std::线程子级(o,std::ref(i));
child.detach();
}
});
返回true;
}
但是,生成的代码毫无意义。生成的线程与while循环竞争。循环递减索引
i
,而线程尝试递增索引。无法保证这些事情何时会发生。增量和减量不是原子的。线程可能会在lambda完成后尝试增加索引

简言之,如果您让它编译,结果是未定义的行为,原因有很多


你到底想做什么?

调用
std::thread
会创建一个元组。无法使用引用初始化元组。因此,您必须使用一个伪引用,
std::ref(i)
来编译它,并使用int-refs
int&
调用可调用项

template <typename callable>
bool thread_starter::start(callable &o)
{
    // Nonsense
    std::thread t(
        [&]() {
        int i = 10;
        while (i-- > 0)
        {
            std::thread child(o, std::ref(i));
            child.detach();
        }
    });
    return true;
}
模板
bool线程启动程序::启动(可调用&o)
{
//胡说八道
螺纹t(
[&]() {
int i=10;
而(i-->0)
{
std::线程子级(o,std::ref(i));
child.detach();
}
});
返回true;
}
但是,生成的代码毫无意义。生成的线程与while循环竞争。循环递减索引
i
,而线程尝试递增索引。无法保证这些事情何时会发生。增量和减量不是原子的。线程可能会在lambda完成后尝试增加索引

简言之,如果您让它编译,结果是未定义的行为,原因有很多


你到底想做什么?

我做了一个类似的程序来弄清楚会发生什么。这就是它的样子:

class mynoncopy 
{
public:

    mynoncopy(int resource)
        : resource(resource)
    {

    }

    mynoncopy(mynoncopy&& other)
        : resource(other.resource)
    {
        other.resource = 0;
    }

    mynoncopy(const mynoncopy& other) = delete;

    mynoncopy& operator =(const mynoncopy& other) = delete;

public:

    void useResource() {}

private:
    int resource;
};

class mycallablevaluearg
{
public:

    void operator ()(mynoncopy noncopyablething)
    {
        noncopyablething.useResource();
    }
};

class mycallableconstrefarg
{
public:

    void operator ()(const mynoncopy& noncopyablething)
    {
        //noncopyablething.useResource(); // can't do this becuase of const :(
    }
};

class mycallablerefarg
{
public:

    void operator ()(mynoncopy& noncopyablething)
    {
        noncopyablething.useResource();
    }
};

class mycallablervaluerefarg
{
public:

    void operator ()(mynoncopy&& noncopyablething)
    {
        noncopyablething.useResource();
    }
};

class mycallabletemplatearg
{
public:

    template<typename T>
    void operator ()(T&& noncopyablething)
    {
        noncopyablething.useResource();
    }
};

我做了一个类似的程序来弄清楚会发生什么。这就是它的样子:

class mynoncopy 
{
public:

    mynoncopy(int resource)
        : resource(resource)
    {

    }

    mynoncopy(mynoncopy&& other)
        : resource(other.resource)
    {
        other.resource = 0;
    }

    mynoncopy(const mynoncopy& other) = delete;

    mynoncopy& operator =(const mynoncopy& other) = delete;

public:

    void useResource() {}

private:
    int resource;
};

class mycallablevaluearg
{
public:

    void operator ()(mynoncopy noncopyablething)
    {
        noncopyablething.useResource();
    }
};

class mycallableconstrefarg
{
public:

    void operator ()(const mynoncopy& noncopyablething)
    {
        //noncopyablething.useResource(); // can't do this becuase of const :(
    }
};

class mycallablerefarg
{
public:

    void operator ()(mynoncopy& noncopyablething)
    {
        noncopyablething.useResource();
    }
};

class mycallablervaluerefarg
{
public:

    void operator ()(mynoncopy&& noncopyablething)
    {
        noncopyablething.useResource();
    }
};

class mycallabletemplatearg
{
public:

    template<typename T>
    void operator ()(T&& noncopyablething)
    {
        noncopyablething.useResource();
    }
};

你还和我们在一起吗?@JiveDadson是的。你还和我们在一起吗?@JiveDadson是的。我正在尝试用不可复制的套接字生成工作线程,但是另一个处理lambdas的类似设置也出现了类似的错误。我对示例代码做了一些修改,希望它能提供比“int”更多的信息修改问题中的代码只会增加更多的编译时错误。它还使这个答案无效,这是正确的。是的,我试图澄清我正在尝试做什么。我试图用不可复制的套接字生成工作线程,但另一个处理lambdas的类似设置也出现了类似的错误。我对示例代码做了一点更改,希望它能提供比“int”更多的信息修改问题中的代码只会增加更多的编译时错误。它也使这个答案无效,这个答案是正确的。是的,我试图澄清我想做什么。