无条件地从构造函数中抛出* [英] Unconditionally throw *this from constructor

查看:121
本文介绍了无条件地从构造函数中抛出*的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我认为从构造函数的内部抛出*这是合法的。我想知道我是否简单地错了(在构造函数完成之前传递对象的问题),或者这是否是风格可恶的。



I发布了第二个更具体的问题 here 已被全面回答。这个帖子中的代码是合法的,如果有点奇怪。



给定一个异常结构:

  struct fail final:public std :: logical_error 
{
fail():std :: logic_error(fail){}
使用std :: logic_error: :logic_error;
};

和一堆电话网站,如:

  throw fail(); 
//或
throw fail(哦亲爱的);

我正在考虑将结构更改为:

  struct fail final:public std :: logic_error 
{
fail():std :: logic_error(fail){throw * this; }
fail(const char * w):std :: logic_error(w){throw * this; }
};

在这一点上,呼叫站点可以保持不变,或者被更改为较短的:

  fail(); 
//或
fail(哦亲爱的);

这本质上意味着我不再需要写在整个地方。我也可以使用名称fail继续捕获异常。这确实似乎有效,但让我怀疑我可能会后悔这个选择。



谢谢



编辑:稍微多一点关于这个行为。



1 / Throw *这将会制作一个*这个副本或移动它,如果这是公平的游戏对于复制检测,所以逻辑错误触发的析构函数不是问题



2 /没有成员的类的默认复制构造函数可能只是复制构造函数base可以复制*这个



3 /通过异常返回的*的副本可能对于未在初始化列表中设置的任何成员有未定义的值



4 /在构建过程中可以调用成员函数。 (默认)复制构造函数是一个成员函数,因此可以在构造过程中调用它。 throw *这将调用copy构造函数。所以我仍然相信代码是合法的

解决方案

我同情你写表达性代码的愿望。
$ b

这是我在代码库中使用的一种东西,特别是当调用C API作为布尔类整数返回成功或失败时。

  #include< stdexcept> 
#include< exception>

struct failure:std :: runtime_error
{
using runtime_error :: runtime_error;
};

template< class Message>
[[noreturn]]
bool fail(Message&& msg)
{
throw failure(std :: forward< Message>(msg));
}

int main()
{
extern bool didSomething();

didSomething()或fail(无法做到);
}

更有趣的例外:

  #include< stdexcept> 
#include< exception>
#include< cstdio>
#include< memory>
#include< system_error>
#include< sstream>
#include< iostream>
#include< iomanip>


命名空间native
{
struct no_message {};

constexpr no_message join(){return {}; }

template< class First,class ... Rest>
std :: string join(First&& first,Rest&& ... rest)
{
std :: ostringstream ss;
ss<<第一;
使用expand = int [];
void(expand {0,
((ss<< rest),0)...
});
return ss.str();
}

[[noreturn]]
void throwSystemError(no_message,int code)
{
throw std :: system_error(code,std :: system_category());
}

template< class Message>
[[noreturn]]
void throwSystemError(Message&& message,int code)
{
throw std :: system_error(code,std :: system_category(),message );
}

模板< class ...零件>
[[noreturn]]
bool systemError(Parts&& ... parts)
{
auto err = errno;
throwSystemError(join(std :: forward< Parts>(parts)...),err);
}

struct file_closer {
void operator()(FILE * fp)const noexcept {
std :: fclose(fp);
}
};
使用FilePtr = std :: unique_ptr< FILE,file_closer> ;;

bool valid(FilePtr const& p){return p.get();

FilePtr openFile(const char * path,const char * mode)
{
auto ptr = FilePtr(std :: fopen(path,mode));
valid(ptr)或systemError(打开文件,std :: quoted(path),in mode,std :: quoted(mode));
return ptr;
}
}


int main()
try
{
auto fptr = native :: openFile(ibetthisdoesntexist .txt,rb);
}
catch(std :: system_error const& syserr)
{
std :: cerr<< 系统错误:
<< syserr.what()
<< ,错误代码<< syserr.code()。value()
<<的std :: ENDL;
std :: exit(100);
}

示例输出:

 系统错误:在rb模式下打开文件ibetthisdoesntexist.txt:没有这样的文件或目录,错误代码2 


I believe it's legal to throw *this from within the body of a constructor. I'd like to know if I'm simply wrong about this (issues passing around an object before it's constructor has completed?) or whether this is stylistically abhorrent.

I posted a second, more specific question here which has been answered comprehensively. The code in this post is legal, if a little odd.

Given an exception struct:

struct fail final : public std::logic_error
{
  fail() : std::logic_error("fail") {}
  using std::logic_error::logic_error;
};

and a bunch of call sites like:

throw fail();
// or
throw fail("oh dear");

I am considering changing the struct to be:

struct fail final : public std::logic_error
{
  fail() : std::logic_error("fail") { throw * this; }
  fail(const char* w) : std::logic_error(w) { throw * this; }
};

At which point the call sites can remain unchanged, or be rewritten as the shorter:

fail();
// or
fail("oh dear");

This essentially means I no longer have to write throw all over the place. I can also continue catch the exception using the name "fail". It certainly seems to work, but leaves me with the suspicion that I may regret this choice later.

Thank you

edit: Thought slightly more about the behaviour.

1/ Throw *this is going to either make a copy of *this or move it if this counts as fair game for copy elision, so the destructor of logic_error firing isn't a problem

2/ The default copy constructor of a class with no members is probably just the copy constructor of the base so can probably copy *this

3/ The copy of *this that gets returned via the exception probably has undefined values for any members that are not set in the initialiser list

4/ Member functions can be called during construction. The (default) copy constructor is a member function so can be called during construction. throw *this will call the copy constructor. So I still believe the code is legal

解决方案

I have sympathy with your desire to write expressive code.

This is the kind of thing I came up with for use in our code base, particularly when calling C APIs that return success or failure as an boolean-like integer.

#include <stdexcept>
#include <exception>

struct failure : std::runtime_error
{
  using runtime_error::runtime_error;
};

template<class Message>
[[noreturn]]
bool fail(Message&& msg)
{
  throw failure(std::forward<Message>(msg));
}

int main()
{
  extern bool didSomething();

  didSomething() or fail("couldn't do it");
}

More fun with exceptions:

#include <stdexcept>
#include <exception>
#include <cstdio>
#include <memory>
#include <system_error>
#include <sstream>
#include <iostream>
#include <iomanip>


namespace native
{
    struct no_message {};

    constexpr no_message join() { return {}; }

    template<class First, class...Rest>
    std::string join(First&& first, Rest&&...rest)
    {
        std::ostringstream ss;
        ss << first;
        using expand = int[];
        void(expand{ 0,
                     ((ss << ' ' << rest),0)...
        });
        return ss.str();
    }

    [[noreturn]]
    void throwSystemError(no_message, int code)
    {
        throw std::system_error(code, std::system_category());
    }

    template<class Message>
    [[noreturn]]
    void throwSystemError(Message&& message, int code)
    {
        throw std::system_error(code, std::system_category(), message);
    }

    template<class...Parts>
    [[noreturn]]
    bool systemError(Parts&&...parts)
    {
        auto err = errno;
        throwSystemError(join(std::forward<Parts>(parts)...), err);
    }

    struct file_closer {
        void operator()(FILE* fp) const noexcept {
            std::fclose(fp);
        }
    };
    using FilePtr = std::unique_ptr<FILE, file_closer>;

    bool valid(FilePtr const& p) { return p.get(); }

    FilePtr openFile(const char* path, const char* mode)
    {
        auto ptr = FilePtr(std::fopen(path, mode));
        valid(ptr) or systemError("opening file", std::quoted(path), "in mode", std::quoted(mode));
        return ptr;
    }
}


int main()
try
{
    auto fptr = native::openFile("ibetthisdoesntexist.txt", "rb");
}
catch(std::system_error const& syserr)
{
    std::cerr << "system error: "
              << syserr.what()
              << ", error code " << syserr.code().value()
              << std::endl;
    std::exit(100);
}

example output:

system error: opening file "ibetthisdoesntexist.txt" in mode "rb": No such file or directory, error code 2

这篇关于无条件地从构造函数中抛出*的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆