使用lambdas将模板类型的成员作为异常抛出

时间:2016-05-09 13:38:32

标签: c++ c++11 lambda

以下是我一直在尝试调查的用例的简单版本

#include<iostream>
#include<stdexcept>
#include<memory>
#include<type_traits>
#include<string>

class intexception:public std::exception
{
    public:

        intexception(std::string m,int x):msg(m),g(x){}

        const char* what() const throw() { return msg.c_str(); }
        virtual ~intexception() throw(){}
        int getExceptInt()const { return g;}

    private:
        std::string msg;
        int g;
};

class Int
{ 
    public: 
        Int():m_ptr(nullptr){}
        Int(const int& x) : m_ptr(new int(x)) { }

        Int(const Int& in) : m_ptr( (in.get()) ? ( new int(*in.m_ptr) ) : ( nullptr ) ) {}
        Int& operator=(const Int&) = default;

        Int(Int&&) = default;
        Int& operator=(Int&&) = default;

        int get() const { return *m_ptr; }         

    private:
        std::unique_ptr<int>m_ptr;
}; 

class TypedInt
{
    public:
    template<typename T, 
        typename std::enable_if< std::is_same<typename std::decay<T>::type,int>::value || std::is_same<typename std::decay<T>::type,long>::value, 
                                 std::nullptr_t>::type = nullptr>

            explicit TypedInt (T& intid )
            : m_holder( intid ),
              m_throw ( [this]() { throw intexception("Integer Exception",static_cast<T>(this->get())); } )
              {}

    TypedInt():m_holder(),m_throw(std::function<void()>()){}

    TypedInt& operator=(TypedInt&&) = default;
    TypedInt(TypedInt&& other):m_holder(std::move(other.m_holder)),m_throw(std::move(other.m_throw)) {}

    TypedInt& operator=(const TypedInt&) = default;
    TypedInt(const TypedInt&) = default;

    int get() const { return m_holder.get(); }

    void Throw() { m_throw(); }

    private:
        Int m_holder;
        std::function<void()>m_throw;
};

void testThrow(TypedInt t)
{
    try
    {
        t.Throw();
    }
    catch(const intexception& e)
    {
        std::cout<<e.what()<<std::endl;
        std::cout<<e.getExceptInt()<<std::endl;
    }
}

int main()
{
    int z = 10;
    TypedInt t1(z);

    TypedInt t2;

    t2 = std::move(t1);

    testThrow(std::move(t2));
    return 0;
}

这没有问题编译好。但是,它最终会出现分段错误。

我看了这个link,我觉得我可能面临类似的问题。

我用gdb调试了这个,我无法理解为什么这个get()函数为我的null成员变量中的基础整数返回std::unique_ptr值。

2 个答案:

答案 0 :(得分:3)

构建t1时,使用以下代码构建其m_throw

m_throw ([this]() {
    throw intexception("Integer Exception",static_cast<T>(this->get()));
})

也就是说,t1.m_throw持有t1(通过指针)。它正在抛出intexception构建的t1.m_holder.get()

当您将其移至t2时,std::function中的实际基础lambda不会发生变化。它仍然试图抛出t1.m_holder.get()。问题是,一旦您从t1.m_holder移出,解除引用基础unique_ptr无效 - 这是您的细分错误的来源。

你必须重新绑定投掷者,以确保你总是从this投掷。最简单的方法是将TypedInt实例作为参数传递,然后您不必担心任何事情。

答案 1 :(得分:2)

您可以在lambda中捕获this的值。在所有移动之后,您捕获的this指针不再指向有效对象。

this捕获不会“跟踪”捕获的对象。

您可以尝试简单地使用实现来捕获intid

m_throw ( [intid]() {
  throw intexception("Integer Exception", static_cast<int>(intid));
} )

否则;如果lambda需要使用当前对象,则在调用lambda时,this指针可以作为参数传入。在这种情况下,鉴于用例,lambda似乎“很重”。

最好避免使用lambda,并在需要时简单地throw;使用throw时对象中的值。