简体   繁体   中英

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-formed 1 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;
}

demo 2

On the other hand, if Data is a non template class 3 , 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(); } void f() const { _data.f(); } fails although its "specialization is never referenced in a context that requires the member definition to exist" .

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, ie Wrapper<void>::f . So in conclusion, snippet #1 is well-formed and snippet #2 is invalid; all compilers work in a standard-conforming manner.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM