9

In the snippet below, I'm puzzled about why the definition of Wrapper::f() const does not make my program ill-formed1 although it calls a non-const member function of a non mutable member variable:

// well-formed program (???)
// build with: g++ -std=c++17 -Wall -Wextra -Werror -pedantic
template<class T> struct Data { void f() {} };

template<class T> struct Wrapper
{
    Data<T> _data;
    void f() const { _data.f(); } // _data.f(): non-const!
};

int main()
{
    Wrapper<void> w; // no error in instantiation point?
    (void) w;
}

demo2

On the other hand, if Data is a non template class3, a diagnostic is issued by my compiler:

// ill-formed program (as expected)
// build with: g++ -std=c++17 -Wall -Wextra -Werror -pedantic
struct Data { void f() {} };

template<class T> struct Wrapper
{
    Data _data;
    void f() const { _data.f(); } //error: no matching function for call to 'Data::f() const'
};

int main()
{
    Wrapper<void> w;
    (void) w;
}

demo

I feel like the answer will contain expressions such as "deduced context" ... but I really cannot pin down the exact part of the standard scecifying this behaviour.

Is there a language lawyer to enlighten me on the matter?


Notes:
1) But I get an error if I try and effectively call Wrapper<T>::f() const.
2) I've compiled with -std=c++17 but this is not specific to C++17, hence no specific tag.
3) In this answer, @Baum mit Augen quotes [N4140, 14.7.1(2)]:

the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist

but here in the compiling snippet (#2) void f() const { _data.f(); } fails although its "specialization is never referenced in a context that requires the member definition to exist".

YSC
  • 38,212
  • 9
  • 96
  • 149
  • I dont think the duplicate covers everything for this question. At least I would expect also the second version to compile according to the asnwer given on the dupe – 463035818_is_not_an_ai Dec 15 '17 at 09:40
  • @tobi303 True, not sure why that is tbh. – Baum mit Augen Dec 15 '17 at 09:45
  • 1
    [\[temp.res\]/8](http://eel.is/c++draft/temp.res#8). See (8.1) in particular. – cpplearner Dec 15 '17 at 10:00
  • @cpplearner 8.1 is about `constexpr if`... – YSC Dec 15 '17 at 10:01
  • 1
    @YSC it is about "template **or** a substatement of a constexpr if statement within a template", though thats about all I understand. standard isnt a language I can understand :( – 463035818_is_not_an_ai Dec 15 '17 at 10:04
  • Welp, looks like @cpplearner is right and my answer is wrong. I forgot about that rule. Do you want to post your own answer or should I fix mine? – Baum mit Augen Dec 15 '17 at 10:04
  • @tobi303 Ho yes, I've read _no valid specialization can be generated for (a template or a substatement) of a constexpr if_ instead of _no valid specialization can be generated for a template or (a substatement of a constexpr if)_; but it doesn't make sense :D my bad – YSC Dec 15 '17 at 10:17
  • relevant: [`[temp.dep]/1`](http://eel.is/c++draft/temp.dep#1) and [`[temp.dep.expr]/5`](http://eel.is/c++draft/temp.dep#expr-5): `_data.f()` in both snippets are _"type dependant expressions"_. Ok, then? – YSC Dec 15 '17 at 10:26
  • I got it wrong, only in snippet with templated `Data` is `_data.f()` a _type dependent expression_. – YSC Dec 15 '17 at 10:39

1 Answers1

4

Snippet #2 is ill-formed.

As already stated in this answer, the template definition of Wrapper::f is well-formed (thus no diagonstics are issued) as long as a valid specialization can be generated.

§17.7/8 [temp.res] states:

Knowing which names are type names allows the syntax of every template to be checked. The program is ill-formed, no diagnostic required, if:

  • no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the template is not instantiated, or [...]

In neither of the two code snippets, Wrapper<void>::f is getting instantiated, because of the rules in §17.7.1/2 [temp.inst]:

The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions, [...].

(emphasizing done by me)

But now §17.7/8 kicks in: if there is no instantiation and there can be no generated specialization for which the template definition of Wrapper::f is valid (which is the case for snippet #2, as for every generated specialization Wrapper<T>::f, a non-const call inside a const function on a member is would be performed), the program is ill-formed and diagnostics are issued.

But because the diagnostics are not mandatory (see §17.7/8 above), the GCC can deny snippet #2 while both VS and clang compile the same code flawlessly.

For snippet #1 however you could provide a user-defined specialization for Data where Data::f is const (say Data<void>::f). Therefore, a valid, generated specialization of Wrapper::f is possible, i.e. Wrapper<void>::f. So in conclusion, snippet #1 is well-formed and snippet #2 is invalid; all compilers work in a standard-conforming manner.

Jodocus
  • 7,493
  • 1
  • 29
  • 45
  • 1
    Reading the edit history of this answer is a truly wild ride. – Griwes Dec 15 '17 at 13:27
  • 1
    @Griwes Ha XD That explains a *lot*! In its (temporary?) final form, this answer deserves upvotes though. – YSC Dec 15 '17 at 13:33
  • @YSC Don't worry, I am pretty sure now this is fine. I have to admit that reading the standard involved me in a learning process, obviously I wasn't à priori sure what the answer might be. – Jodocus Dec 15 '17 at 20:26