Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Well-formed program containing an ill-formed template member function?

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".

like image 524
YSC Avatar asked Dec 15 '17 09:12

YSC


1 Answers

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.

like image 52
Jodocus Avatar answered Nov 01 '22 06:11

Jodocus