18

Consider the following code:

template <typename T> int foo();
template <typename T> int foo() = delete;

is this valid C++11?

  • GCC (9.1) says: Yes!
  • clang (8.0) says: No!
  • nvcc (9.2) says: No!
  • MSVC (19.20) says: Yes! (in C++14 mode, it doesn't support C++11.)

... see it all on GodBolt.

so which compilers are right and which compilers are s@#$%e ? :-)

einpoklum
  • 118,144
  • 57
  • 340
  • 684

1 Answers1

22

GCC and MSVC have a bug.

[dcl.fct.def.delete]

4 ... A deleted definition of a function shall be the first declaration of the function or, for an explicit specialization of a function template, the first declaration of that specialization...

Which I believe stands for instantiated declarations and definitions too. Since referring to a deleted function is a hard error, it must be declared as deleted asap.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • Why "must" it be declared as deleted ASAP - as opposed to - before it is first referred to? Also, even if a deletion appeared later, a compiler could notice the error when it sees the deletion instead of when it sees the reference. But - accepting. – einpoklum Jun 01 '19 at 20:50
  • Also - is this still the case in C++14 and C++17? Just curious and I don't want to open another question. – einpoklum Jun 01 '19 at 20:55
  • 1
    Bug report [filed](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90703) against GCC. – einpoklum Jun 01 '19 at 21:04
  • 3
    @einpoklum - The code referring to it could be between the first declaration and definition. Demanding it on the first declaration is just easiest I suppose. And seeing as I quoted n4659 (C++17, haven't noticed the tag), yes it is. I checked n3337 (c++11), the wording is the same. – StoryTeller - Unslander Monica Jun 01 '19 at 21:52
  • @einpoklum: A virtual function has to reserve a slot in the vtable (in normal implementations). Having it declared as non-deleted in one declaration and non-deleted in another might be considered incompatible declarations because of actual layout of something? (Whether a function is virtual or not, you could imagine some strange implementation where some kind of mechanism is triggered for non-deleted function declarations.) Or maybe this rule makes it easier to parse, if you don't have to support changing a non-deleted function to deleted after the fact. – Peter Cordes Jun 02 '19 at 09:31
  • @PeterCordes - A deleted definition is explicitly forbidden for virtual function overrides. You can't refer to a deleted function via a virtual call. It's purely a compile time thing. – StoryTeller - Unslander Monica Jun 02 '19 at 09:32
  • @StoryTeller: Ok, that rules out part of my wild guess. But you could imagine a C++ implementation that did *something* for every non-deleted function it saw a declaration for. A vtable would only be a loose analogy. (Or the C++ standards committee could imagine that such an implementation *might* exist, and wanted to make things easier for it.) – Peter Cordes Jun 02 '19 at 09:53
  • @PeterCordes: 1. C++ does not know anything about vtables and an implementation is not required to have any of those at all. 2. I'm talking about freestanding functions here; member functions are slightly different beasts. 3. Would you really allocate vtable slots when encountering member function _template_ declarations? Wouldn't that mean an infinite number of vtable slots? – einpoklum Jun 02 '19 at 14:19
  • @einpoklum: Like I already said, a vtable is merely an example of something an implementation might process when encountering declarations. Maybe it's too much of a stretch to imagine the C++ committee worrying about implementations that might have trouble with this mismatch *for any reason like this, not specifically vtables*; maybe they just forbid it because it would be inconsistent. – Peter Cordes Jun 02 '19 at 15:55
  • @einpoklum What if the deleted declaration is in another TU? It's okay for defaulted functions, but not for deleted. – Rakete1111 Jun 02 '19 at 16:13
  • @Rakete1111: What happens in another TU is irrelevant for compilation... – einpoklum Jun 02 '19 at 16:21
  • @einpoklum ? What do you mean? – Rakete1111 Jun 02 '19 at 16:22
  • One could argue that the cited paragraph talks about functions, and here we have a function template (I'm not sure I'm willing to be that one :-) ). The Standard in its current form allows *some* templates to be first declared and later defined as deleted: member function templates of a class template (since the implicit instantiation of the enclosing class template triggers the instantiation of declarations, but not definitions, of member templates). I wonder what the actual intent is here. As an additional data point, EDG in strict mode also rejects the code. cc @einpoklum – bogdan Jun 02 '19 at 16:27
  • @bogdan - I'm not sure that not instantiating the definition is an allowance in this rule. It seems like only a delay for the point at which it must be checked. And as a data point on this, clang rejects an out of class definition outright https://wandbox.org/permlink/Iv0yelOWnx4Y7yaN – StoryTeller - Unslander Monica Jun 03 '19 at 05:33
  • @Rakete1111: You only compile a single TU at a time, and any files going into another TUs are not considered during compilation. – einpoklum Jun 03 '19 at 09:59
  • @einpoklum What I mean is that if you declare the function in one TU, then that TU will think that there is a definition of it. If you then delete it in another one, the first TU has no idea that the function is actually deleted. – Rakete1111 Jun 03 '19 at 10:00
  • @Rakete1111: This is "not a problem" in the same sense that you could have conflicting definitions of a function in different TUs, and each will compile. It's on you to make sure this doesn't happen, anyway. – einpoklum Jun 03 '19 at 10:13
  • @StoryTeller As far as I can tell, those are treated as real declarations and definitions, respectively, even if we're not allowed to write them. For example, looking at [DR2260](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#2260), it seems that it was introduced to avoid issues related to this separation between declaration and definition points. Your example uses member functions, not member function templates. That being said, a similar example using templates is also rejected; moreover, it's also rejected by GCC :-). – bogdan Jun 03 '19 at 13:15
  • We also have to consider the fact that requiring the deleted definition to be the first declaration for a template is not as useful as it is for non-template functions. You can have a deleted definition for the template, and then a non-deleted explicit specialization. This means that the compiler still has to check everything between the first template declaration and the point of instantiation to decide what definition to use. – bogdan Jun 03 '19 at 13:17
  • Issues like trying to use a specialization before an explicit specialization is seen for it (either in the same TU or across TUs) are handled by paragraphs like [\[temp.point\]/7](http://eel.is/c++draft/temp.point#7), which make them ill-formed, NDR. – bogdan Jun 03 '19 at 13:17
  • 1
    @bogdan - True. But then again it's nothing new when it comes to templates. One has to bring up temp.expl.spec/7 whenever this subject comes up :) – StoryTeller - Unslander Monica Jun 03 '19 at 13:18
  • Yeah, what I meant was that we have such provisions for templates, so allowing a declaration before the deleted definition wouldn't introduce more problems (that I can see...). However, for a non-template function, once you've seen a (non-defining) declaration, it's OK to use it, there has to be a definition somewhere. Allowing that definition to be deleted would break the "it's OK to use it" part, so the rule makes sense for functions. I wonder if that's why templates are not specifically mentioned there, and also why they're specifically left out of the fix in the DR I mentioned above. – bogdan Jun 03 '19 at 13:40