public inbox for gcc-bugs@sourceware.org
help / color / mirror / Atom feed
From: "f.heckenbach@fh-soft.de" <gcc-bugzilla@gcc.gnu.org>
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	[thread overview]
Message-ID: <bug-104577-4-KKawVU4ZW1@http.gcc.gnu.org/bugzilla/> (raw)
In-Reply-To: <bug-104577-4@http.gcc.gnu.org/bugzilla/>

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104577

--- Comment #3 from Frank Heckenbach <f.heckenbach@fh-soft.de> ---
(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=1 for the full
> 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 template
> parameter object, whose value is that of the corresponding template argument
> after it has been converted to the type of the template-parameter. ..."
> 
> 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 lvalue
> object of type const S.
> 
> 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). 
(which you then demonstrate using static_cast)

As I said I'm not a standard expert, but if that's so, shouldn't this program
also require a copy/move constructor? GCC accepts it without one.

struct S
{
  constexpr S () = default;
  S (S &&) = delete;
};

int main ()
{
  static_cast <S> (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 &&) = delete;
  operator int () const { return 0; }
};

template <S s> int i = s;

int main ()
{
  return i <0>;
}

Now the template argument is either an empty braced-list or the integer 0 and
converting it to S does not require a copy/move constructor, but rather the
default constructor or the constructor taking an int, respectively. Still GCC
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 explanation
(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 don't
claim that it is the case here, but I know there are cases where a constructor
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.

  parent reply	other threads:[~2022-12-28  1:30 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-02-17  3:55 [Bug c++/104577] New: needs copy constructor to call method of " f.heckenbach@fh-soft.de
2022-02-18  1:14 ` [Bug c++/104577] needs copy constructor for " pinskia at gcc dot gnu.org
2022-12-26  4:19 ` mail at jhellings dot nl
2022-12-28  1:30 ` f.heckenbach@fh-soft.de [this message]
2023-02-11  4:35 ` herring at lanl dot gov
2023-06-05 19:29 ` ppalka at gcc dot gnu.org

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=bug-104577-4-KKawVU4ZW1@http.gcc.gnu.org/bugzilla/ \
    --to=gcc-bugzilla@gcc.gnu.org \
    --cc=gcc-bugs@gcc.gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).