From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 48) id AB4B93858D37; Wed, 28 Dec 2022 01:30:03 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org AB4B93858D37 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1672191003; bh=pdW62mWe1LwU1c/LkhZmz/NP/MINjCETTKV49xt0nHs=; h=From:To:Subject:Date:In-Reply-To:References:From; b=IZb76lGkfDcea3jG8yU9D48rzRgifAfGhfQVj72Udh9LTXopJQsHuDJwW5uaWMUVr AaHAA0n87DAg09hBfbd1j3pMhm3z9/LwBT95fTYoz16tQcW/cjdhof5QsHyir2sOc4 8SuXCx8L1H0FzbR1Ufq+dxlMeXHVHxISKXL2FSpw= From: "f.heckenbach@fh-soft.de" To: gcc-bugs@gcc.gnu.org Subject: [Bug c++/104577] needs copy constructor for class non-type template parameter Date: Wed, 28 Dec 2022 01:30:00 +0000 X-Bugzilla-Reason: CC X-Bugzilla-Type: changed X-Bugzilla-Watch-Reason: None X-Bugzilla-Product: gcc X-Bugzilla-Component: c++ X-Bugzilla-Version: 11.2.0 X-Bugzilla-Keywords: rejects-valid X-Bugzilla-Severity: normal X-Bugzilla-Who: f.heckenbach@fh-soft.de X-Bugzilla-Status: NEW X-Bugzilla-Resolution: X-Bugzilla-Priority: P3 X-Bugzilla-Assigned-To: unassigned at gcc dot gnu.org X-Bugzilla-Target-Milestone: --- X-Bugzilla-Flags: X-Bugzilla-Changed-Fields: Message-ID: In-Reply-To: References: Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable X-Bugzilla-URL: http://gcc.gnu.org/bugzilla/ Auto-Submitted: auto-generated MIME-Version: 1.0 List-Id: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=3D104577 --- Comment #3 from Frank Heckenbach --- (In reply to mail from comment #2) > I looked a bit further into this and into what the standard says. GCC does > partially the correct thing in this case, whereas several other compilers= do > the wrong thing. See https://jhellings.nl/article?articleid=3D1 for the f= ull > analysis. I'm not a standard expert. However, I don't think your analysis applies to quite the same situation. Your test code uses two templates with non-type parameters, test_cases and print_nttp, and I think the critical copy occurs= at the point where the value is passed from one to the other. If I avoid the latter template by turning it into "void print_nttp(const copy_counter &C)"= and adjusting the callers accordingly, GCC outputs "0 0 0 0 1" as I'd expect. > The short summary: > In Clause 8 of Section [temp.param], the standard defines the value of a > non-type template argument: > "An id-expression naming a non-type template-parameter of class type T > denotes a static storage duration object of type const T known as a templ= ate > parameter object, whose value is that of the corresponding template argum= ent > after it has been converted to the type of the template-parameter. ..." >=20 > Hence, whatever is provided as a non-type template parameter argument (of > type S in this bug report) is converted to the type S and the value > resulting from this conversion is available within the template as an lva= lue > object of type const S. >=20 > To convert an expression to type S, you either need a constexpr copy > constructor (general case) or a constexpr move constructor (in the special > case in which you provide a movable value).=20 (which you then demonstrate using static_cast) As I said I'm not a standard expert, but if that's so, shouldn't this progr= am also require a copy/move constructor? GCC accepts it without one. struct S { constexpr S () =3D default; S (S &&) =3D delete; }; int main () { static_cast (S{}); } But let's assume it does require one. This would then indeed apply to my original example which uses "S{}" as the template argument (which then may = need to be copied/moved), but it wouldn't apply if I change it to just "i <{}>",= or to an int such as this: struct S { constexpr S (int) { } S (S &&) =3D delete; operator int () const { return 0; } }; template int i =3D s; int main () { return i <0>; } Now the template argument is either an empty braced-list or the integer 0 a= nd converting it to S does not require a copy/move constructor, but rather the default constructor or the constructor taking an int, respectively. Still G= CC requires a copy/move constructor. Further notes: - If converting implies copying as you say, shouldn't GCC give 2 in the last row? First you make an explicit copy (having count 1), then it's converted = to the argument of print_nttp which should yield in count 2, shouldn't it? - The list of possible explanations given under "Dissecting the issue of copy_counter" is not exhaustive: * As you briefly mention in the text below, possible changes in the standard between what the given compiler versions implement could be another explana= tion (which may be relevant since the feature is rather new and the respective standards have not been fully implemented by some or all of the given compilers). * Also possibly relevant, there's also the possibility that the program is correct, but the standard does allow different results. I don't know and do= n't claim that it is the case here, but I know there are cases where a construc= tor must be available, but may or may not be actually called (copy elision). So testing whether it is called may not not necessarily give the same answer as finding out whether a constructor must be available.=