C++ Logo

std-discussion

Advanced search

Re: Throwing noncopyable temporaries

From: Lénárd Szolnoki <cpp_at_[hidden]>
Date: Fri, 02 Sep 2022 09:32:19 +0100
Hi,

On 2 September 2022 07:19:35 BST, Jens Maurer via Std-Discussion <std-discussion_at_[hidden]> wrote:
>On 01/09/2022 13.55, Schneider, Robert via Std-Discussion wrote:
>> Hi,
>>
>> GCC and clang at the moment allow throwing of noncopyable prvalue expressions:
>>
>> struct foo
>> {
>> foo() = default;
>> foo(foo const&) = delete;
>> foo(foo&&) = delete;
>> };
>>
>> void bar()
>> {
>> throw foo();
>> }
>> https://compiler-explorer.com/z/d7h8f8ror
>>
>> I was wondering if that's intended, since I can't quite relate it to the wording of except.throw#5:
>>
>>> When the thrown object is a class object, the constructor selected for the copy-initialization as well as the constructor selected for a copy-initialization considering the thrown object as an lvalue shall be non-deleted and accessible, even if the copy/move operation is elided ([class.copy.elision]).
>>
>> As far as I understand, mandatory copy elision means that the constructor selected for copy-initialization in this case is the _default constructor_. Copy-initialization doesn't even consider copy/move constructors here. The part about "the thrown object as an lvalue" doesn't make sense to me in this context.
>
>When we throw an exception, we want to make sure it can be caught:
>
>void bar()
>{
> try {
> throw foo();
> } catch (foo x) { // invokes copy constructor using an lvalue "foo"
>
> }
>}
>
>This situation could also be handled by making the handler ill-formed.

From the wording it's not clear to me if it's the throw expression and/or the handler should be ill-formed. As throw is an expression, it can be used in SFINAE context too. IMO this should work, but it doesn't:

template <typename T>
concept throwable = requires(T (*fptr)()) {
    throw fptr();
};

Probably throw and catch should be ill-formed at the same time.

>The interesting aspect is that gcc differs in its behavior depending on
>whether S has data members or not:
>
>
>struct S
>{
> S() = default;
> S(const S&) = delete;
> // int x = 0; // #1
>};
>
>int main()
>{
> try {
> throw S();
> } catch (S s) {
> return 1;
> }
>}
>
>
>but if "int x" is present, I get:
>
>
>x.cc:13:14: error: use of deleted function ‘S::S(const S&)’
> 13 | } catch (S s) {
> | ^
>x.cc:5:3: note: declared here
> 5 | S(const S&) = delete;
> | ^
>
>
>That feels seriously inconsistent.
>
>
>> Anyway, was this change intended? Should the example above compile?
>
>The example you gave should not compile, per the current wording.
>
>Jens
>--
>Std-Discussion mailing list
>Std-Discussion_at_[hidden]
>https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion

Received on 2022-09-02 08:32:25