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".
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With