移动 std::shared_ptr 导致程序崩溃

3
我需要为一项工作构建一个小型OpenGL包装器。我试图避免为所有我的类编写复制构造函数和赋值函数。
最懒惰且不写复制的方法是使用指针,但由于指针是邪恶的,我尝试仅使用std :: shared_ptr。
问题在于,通过使用通过值接收std :: shared_ptr的构造函数,我的程序会崩溃,并且当使用完美转发时,仅在传递左值时才起作用。
// this class doesn't have any default, copy constructors.
class Dep
{
    Dep(std::string path, GLenum type);
};

class Program
{
std::shared_ptr<Dep> dep1;
std::shared_ptr<Dep> dep2;

(...)

我尝试了两种不同的构造函数:

template <class T, class = typename std::enable_if<std::is_constructible<std::shared_ptr<Dep>, T>::value>::type>
Program(T&& dep1, T&& dep2)
: dep1(std::forward<T>(dep1)), dep2(std::forward<T>(dep2))
{
}

另一个

Program(std::shared_ptr<Dep> dep1, std::shared_ptr<Dep> dep2)
: dep1(std::move(dep1)), dep2(std::move(dep2))
{
}

我想要做的是能够传递左值或右值共享指针,但是除非我在前向声明中使用左值,否则它会崩溃。

// passing these work on the std::forward one, but that's the only case it works
// if i try to use std::make_shared as parameter (for rvalue) it crashes on both
// the std::move and std::forward ones.
auto vs = std::make_shared<GLShader>("TriangleVS.glsl", GL_VERTEX_SHADER);
auto fs = std::make_shared<GLShader>("TriangleFS.glsl", GL_FRAGMENT_SHADER);

简述:std::forward中的左值引用可行,右值引用不可行。std::move中的左值引用或右值引用都不可行。当调用std::shared_ptr构造函数(在程序构造函数内部)时,程序会挂起。

我看了Scott Mayers的通用引用讲座,觉得自己理解了,结果还是出了这个问题。

1个回答

1

我认为这段代码没有任何问题,并且在http://ideone.com/jlShgB上测试也没有问题:

#include <memory>
#include <utility>
#include <string>
#include <cassert>

enum GLenum { foo };

// this class doesn't have any default, copy constructors.
struct Dep
{
    Dep(std::string path, GLenum type) {}
    Dep() = delete;
    Dep(Dep const&) = delete;
};

struct Program
{
    std::shared_ptr<Dep> dep1;
    std::shared_ptr<Dep> dep2;

#if 1
    template <class T, class = typename std::enable_if<std::is_constructible<std::shared_ptr<Dep>, T>::value>::type>
    Program(T&& dep1, T&& dep2)
        : dep1(std::forward<T>(dep1)), dep2(std::forward<T>(dep2))
    {
    }
#else
    Program(std::shared_ptr<Dep> dep1, std::shared_ptr<Dep> dep2)
        : dep1(std::move(dep1)), dep2(std::move(dep2))
    {
    }
#endif
};

int main()
{
    auto dep1 = std::make_shared<Dep>("dep1", foo);
    auto dep2 = std::make_shared<Dep>("dep2", foo);
    Program p(std::move(dep1), std::move(dep2));

    assert(!dep1 && !dep2);
}

当然,如果您将 #if 1 更改为 #if 0,断言会引发异常,因为 dep1/dep2 将没有被移动。
这导致我怀疑其他地方存在问题。如果您可以隔离出表现出问题的 SSCCE,请告诉我。

非常感谢您的回答。那段代码就像我的代码一样,事实上我刚刚在我的编译器vs2012上尝试了一下,效果很好!也许与Dep类有关?也许是它的构造函数?Dep类是一个在其构造函数中执行大量初始化工作的类(但它从不初始化内存或shared_ptr),因为它是一个必须在初始化时知道将要工作的类。但我之前测试过它,效果很好。Program类只是一个包含2个Dep的小容器,所以不可能是那里出了问题。我会尝试隔离问题,然后再在这里寻求帮助。再次感谢。 - sap
好的,我找到了问题所在。在Program类中,我在移动后访问了一个参数成员,而不是使用带有“this->”的类成员(我不太喜欢使用this->,但现在我会用了!)。再次感谢您的帮助,我接受您的答案,因为它对我来说是一个很好的未来参考,用于构建接受std::shared_ptr的构造函数。再次感谢。 - sap
@sap 感谢反馈。我希望 'assert' 的想法能够有所帮助。另外,我通常会将成员名称设置为唯一的,比如 _dep1(dep1)_dep1(arg1) - sehe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接