C++ compiler does not detect error in class template - c++

Consider the following example:
template <class T>
class C
{
public:
C();
C(C&& rhs);
private:
T m_data;
};
template <class T>
C<T>::C()
: m_data(T())
{
}
template <class T>
C<T>::C(C&& rhs)
: m_data(rhs.data)
{
}
int main()
{
C<int> i;
}
Line : m_data(rhs.data) contains an error as C does not have a member named data. But none of the compilers that I tried (gcc 5.2, clang 3.5.1) detected that error.
But when I add the following line to main function the compiler detects the error:
C<int> j = std::move(i);
Why the compiler does not give an error in the first case?
Even if that particular function is not called it can figure out that C has no member named data.
In addition when I change the definition of move constructor to the following:
template <class T>
C<T>::C(C&& rhs)
: m_data(rhs.data)
{
data = 0;
}
the compiler gives error on line data = 0; but not on : m_data(rhs.data).
So the function gets parsed.

There is 2 passes to check error in template.
One for non dependent code
One for dependent code (done at instantiation)
Your code is template dependent, so it is checked only when the method is instantiated.

Your template is ill-formed but the error does not require a diagnostic (in other words, the compiler is allowed to not give an error message and can do anything it wants). More precisely, the Standard says
Similarly, if the id-expression in a class member access expression for which the type of the object expression is the current instantiation does not refer to a member of the current instantiation or a member of an unknown specialization, the program is ill-formed even if the template containing the member access expression is not instantiated; no diagnostic required.
In your code C is the current instantiation and rhs.data is a class member access expression but does not refer to a member of the current instantiation and not to a member of an unknown specialization (which would be the case if C had dependent base classes, i.e if you would have written class C : T or something similar).
To read up on these rules, see this answer . It is also worth nothing that this kind of code has always been ill-formed (no diagnostic required), even in C++03 that didn't have this addition rule that I quoted. Because this code makes the template have no valid instantiation, for all possible types of T. But the existing rule of C++03 is rather broad and this addition of C++11 is a succinct test that allows this kind of code to be safely rejected.

the compiler does not try to compile C(C&& rhs); since you don't call it (in the first try).
this is called the zero overhead rule in C++ - you don't pay on what you don't use.
when you try to call it the compiler than tries to compile the function and fails.

In this case, the compiler obiously couldn't figure out that the move constructor will never work. It is allowed to do so, but not required.
In the general case, it is hard to detect that there is no T whatsoever for which the code could be compiled. If it just fails for C<int> and C<float>, but works for C<my_class>, the compiler must not complain (as long as the function isn't used for int or float).

Related

Concept is satisfied by a type that would seemingly produce an invalid expression

In the following code, the can_foo concept tests whether or not a foo() member function can be called on an instance of a type. I will use it to test instances of two templates: base conditionally enables the foo member function, and derived overrides foo to call into its base's implementation:
template <typename T>
concept can_foo = requires(T v) {
v.foo();
};
template <bool enable_foo>
struct base {
void foo()
requires enable_foo
{}
};
template <typename T>
struct derived : T {
void foo()
{
static_cast<T&>(*this).foo();
}
};
If I test whether instances of the base template satisfy the concept, it does what I would expect:
static_assert(can_foo<base<true>>); //okay
static_assert(not can_foo<base<false>>); //okay
When I wrap those types in derived, I see:
static_assert(can_foo<derived<base<true>>>); //okay
static_assert(not can_foo<derived<base<false>>>); //error: static assertion failed
This is surprising! I expected that derived<base<false>> would not satisfy can_foo - its definition of foo uses an expression that isn't valid given T = base<false>, and using the same expression tested by the concept in an evaluated context results in an error that says as much:
int main()
{
derived<base<false>> v{};
v.foo(); //error
}
The error message isn't at the call site, which is probably relevant; it references the body of derived<>::foo. From clang:
<source>:18:32: error: invalid reference to function 'foo': constraints not satisfied
static_cast<T&>(*this).foo();
^
<source>:31:7: note: in instantiation of member function 'derived<base<false>>::foo' requested here
v.foo(); //"invalid reference to function 'foo'"
^
<source>:10:18: note: because 'false' evaluated to false
requires enable_foo
^
clang: https://godbolt.org/z/vh58TTPxo
gcc: https://godbolt.org/z/qMPrzznar
Both compilers produce the same results, so I assume the problem is that there's a subtlety in the standard that I'm missing. Adding a can_foo<T> constraint to derived<T> or derived<T>::foo "fixes" this (that is, derived<base<false>> will no longer satisfy can_foo), and in a code review I would argue that this constraint should be present - but this is surprising behaviour nonetheless and I'd like to understand what's going on.
So: why does derived<false> satisfy can_foo?
A requires-expression can only detect invalid constructs in the "immediate context" of the expression that is tested. In particular
requires(T v) {
v.foo();
};
will not check whether it is actually well-formed to have the call v.foo(). If v.foo() would be ill-formed due to an ill-formed construct inside the body of the foo function, this will not be detected by the requires-expression because the body is not in the immediate context.
The question is, what should happen next? Should the requires-expression go and instantiate the body of foo and give a hard error, or should it return true and give you a hard error later when you attempt to call v.foo()? The answer is the second one: instantiation is not performed because it is not required. See [temp.inst]/5
Unless a function template specialization is a declared specialization, the function template specialization is implicitly instantiated when the specialization is referenced in a context that requires a function definition to exist or if the existence of the definition affects the semantics of the program. [...]
[temp.inst]/11 additionally implies that the implementation is not permitted to instantiate the definition unless it is required.
In an unevaluated context, calling v.foo() does not require the definition of foo to exist, because it is not odr-used unless it is potentially evaluated and it is normally the ODR that requires a definition to exist. (However, there are two situations where referencing a function requires its definition to exist even in an unevaluated context: when the function has a deduced return type or is needed for constant evaluation ([temp.inst]/8)). Since the definition is not required to exist, the definition is not instantiated.
You might want to modify derived::foo so that it propagates the constraint from T::foo, and thus the ill-formedness can be detected by can_foo<derived<T>>:
void foo() requires can_foo<T> {
static_cast<T&>(*this).foo();
}
derived has a foo for every template parameter T, it just cannot always be instantiated. When trying to instantiate derived<base<false>>::foo where you try to to call it in main, you get the error message that the instantiation is not valid since foo cannot be called for the type base<false>.
Rules regarding when instantiation is optional and/or required in the standard would be [temp.inst].
I'm however not able to point to a conclusive passage that says that
checking whether derived<base<false>>::foo exists in the context of the static_assert does not require instantiation. Is it the same as overload resolution checking? "If the function selected by overload resolution can be determined without instantiating a class template definition, it is unspecified whether that instantiation actually takes place."
If you want derived::foo to only be available if it can be instantiated, you could require can_foo on its T.
template <typename T>
struct derived : T {
void foo() requires can_foo<T>
{
static_cast<T&>(*this).foo();
}
};
https://godbolt.org/z/EqrMq4Moq

Must destructor be just available (public) or fully valid for default initialized class members?

Please consider a struct A having a field u of type U<R> with a default initializer. The destructor ~U<R> is only declared:
template<typename T>
struct U {
~U();
};
struct R;
struct A {
U<R> u = U<R>{};
};
All compilers accept this code, demo: https://gcc.godbolt.org/z/oqMjTovMo
But if we define the destructor ~U<R> as follows:
template<typename T>
struct U {
~U() { static_assert( sizeof(T) > 0 ); }
};
then the current compilers diverge. MSVC keeps accepting the program, while GCC/Clang print the error
error: invalid application of 'sizeof' to an incomplete type 'R'
demo: https://gcc.godbolt.org/z/713TzPd6v
Obviously, the compiler must verify destructor availability of default initialing class members in case an exception occurs during construction. But does the standard require that the compiler just check the availability of the destructor (as MSVC does), or the compiler should verify its body as well? MSVC behavior looks more convenient here since it permits forward declaration of R by the moment of struct A definition.
P. S. This questing has not only purely theoretical interest. If one replaces U here with std::unique_ptr then it explains why class fields of type std::unique_ptr<R> are accepted by MSVC and rejected by GCC/Clang for incomplete classes R.
[dcl.init.aggr]/8: (emphasis mine)
The destructor for each element of class type is potentially invoked ([class.dtor]) from the context where the aggregate initialization occurs.
[basic.def.odr]/8:
A destructor for a class is odr-used if it is potentially invoked.
[basic.def.odr]/10:
Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program outside of a discarded statement; no diagnostic required.
So ~U() is potentially invoked at U<R> u inside A, which requires its definition.
Then according to [temp.inst]/4:
Unless a member of a class template or a member template is a declared specialization, the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist ...
Bonus note: and of course, it is actually invoked whenever an instance of A is destroyed. So it needs to be compiled.
Note: in the first version the compilers accept the code because the destructor is not inline, so it fails during linking (example).
As for MSVC accepting the code, it appears to be a bug.

One static_assert makes another static_assert fail (is_default_constructible for inner class) [duplicate]

I have those classes:
#include <type_traits>
template <typename T>
class A {
public:
static_assert(std::is_default_constructible_v<T>);
};
struct B {
struct C {
int i = 0;
};
A<C> a_m;
};
int main() {
A<B::C> a;
}
When compiling, a_m is not default constructible but a is.
When changing C to:
struct C {
int i;
};
everything is fine.
Tested with Clang 9.0.0.
This is disallowed both by the text of the standard and by several major implementations as noted in the comments, but for completely unrelated reasons.
First, the "by the book" reason: the point of instantiation of A<C> is, according to the standard, immediately before the definition of B, and the point of instantiation of std::is_default_constructible<C> is immediately before that:
For a class template specialization, [...] if the specialization is
implicitly instantiated because it is referenced from within another
template specialization, if the context from which the specialization
is referenced depends on a template parameter, and if the
specialization is not instantiated previous to the instantiation of
the enclosing template, the point of instantiation is immediately
before the point of instantiation of the enclosing template.
Otherwise, the point of instantiation for such a specialization
immediately precedes the namespace scope declaration or definition
that refers to the specialization.
Since C is clearly incomplete at that point, the behavior of instantiating std::is_default_constructible<C> is undefined. However, see core issue 287, which would change this rule.
In reality, this has to do with the NSDMI.
NSDMIs are weird because they get delayed parsing - or in standard parlance they are a "complete-class context".
Thus, that = 0 could in principle refer to things in B not yet declared, so the implementation can't really try to parse it until it has finished with B.
Completing a class necessitates the implicit declaration of special member functions, in particular the default constructor, as C doesn't have a constructor declared.
Parts of that declaration (constexpr-ness, noexcept-ness) depend on the properties of the NSDMI.
Thus, if the compiler can't parse the NSDMI, it can't complete the class.
As a result, at the point when it instantiates A<C>, it thinks that C is incomplete.
This whole area dealing with delayed-parsed regions is woefully underspecified, with accompanying implementation divergence. It may take a while before it gets cleaned up.
Undefined behavior it is:
If an instantiation of a template above depends, directly or
indirectly, on an incomplete type, and that instantiation could yield
a different result if that type were hypothetically completed, the
behavior is undefined.

Trying to understand templates and name lookup

I am trying to understand the following code snippets
Snippet #1
template <typename T>
struct A
{
static constexpr int VB = T::VD;
};
struct B : A<B>
{
};
Neither gcc9 nor clang9 throw an error here.
Q. Why does this code compile? Aren't we instantiating A<B> when inheriting from B? There is no VD in B, so shouldn't the compiler throw an error here?
Snippet #2
template <typename T>
struct A
{
static constexpr auto AB = T::AD; // <- No member named AD in B
};
struct B : A<B>
{
static constexpr auto AD = 0xD;
};
In this case, gcc9 compiles fine but clang9 throws an error saying "No member named AD in B".
Q. Why does it compile with gcc9/why doesn't it compile with clang9?
Snippet #3
template <typename T>
struct A
{
using TB = typename T::TD;
};
struct B : A<B>
{
using TD = int;
};
Here both clang9 and gcc9 throw an error. gcc9 says "invalid use of incomplete type 'struct B'".
Q. If struct B is incomplete here then why is it not incomplete in snippet #2?
Compiler flags used: -std=c++17 -O3 -Wall -Werror. Thanks in Advance!!!
I believe these essentially boil down to [temp.inst]/2 (emphasis mine):
The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions, default arguments, or noexcept-specifiers of the class member functions, member classes, scoped member enumerations, static data members, member templates, and friends; […]
and [temp.inst]/9
An implementation shall not implicitly instantiate […] a static data member of a class template […] unless such instantiation is required.
The wording in the standard concerning implicit template instantiation leaves many details open to interpretation. In general, it would seem to me that you simply cannot rely on parts of a template not being instantiated unless the specification explicitly says so. Thus:
Snippet #1
Q. Why does this code compile? Aren't we instantiating A when inheriting from B? There is no VD in B, so shouldn't the compiler throw an error here?
You are instantiating A<B>. But instantiating A<B> only instantiates the declarations, not the definitions of its static data members. VB is never used in a way that would require a definition to exist. The compiler should accept this code.
Snippet #2
Q. Why does it compile with gcc9/why doesn't it compile with clang9?
As pointed out by Jarod42, the declaration of AB contains a placeholder type. It would seem to me that the wording of the standard is not really clear on what is supposed to happen here. Does the instantiation of the declaration of a static data member that contains a placeholder type trigger placeholder type deduction and, thus, constitute a use that requires the definition of the static data member? I can't find wording in the standard that would clearly say either yes or no to that. Thus, I would say that both interpretations are equally valid here and, thus, GCC and clang are both right…
Snippet #3
Q. If struct B is incomplete here then why is it not incomplete in snippet #2?
A class type is only complete at the point where you reach the closing } of the class-specifier [class.mem]/6. Thus, B is incomplete during the implicit instantiation of A<B> in all your snippets. It's just that this was irrelevant for Snippet #1. In Snippet #2, clang did give you an error No member named AD in B as a result. Similar to the case of Snippet #2, I can't find wording on when exactly member alias declarations would be instantiated. However, unlike for the definition of static data members, there is no wording in place to explicitly prevent the instantiation of member alias declarations during implicit instantiation of a class template. Thus, I would say that the behavior of both GCC and clang is a valid interpretation of the standard in this case…

What does a compiler check for uninstantiated template code?

For example, the following code piece compiles with gcc-4.9 and clang-602
class Base
{
public:
static void foo() {}
void badfoo(int i) {}
};
template <typename T>
class Derived : public Base
{
public:
void bar() { Base::foo(); }
void badbar() { Base::badfoo(); } // compiles ok
//static void badbar() { Base::badfoo(); } // compile error
//void worsebar() { Base::nonexist(); } // compile error
};
int main()
{
return 0;
}
But the commented out lines won't compile.
My questions are:
Why badbar() compiles but worsebar() doesn't ?
If I change badbar() to static it won't compile either, regardless if base::badfoo is static or not.
Does the standard say anything about what should be checked in this situation ? gcc4.4 actually refuses to compile even badbar().
Update:
Problem 1 has been explained by a number of answers, but it seems compilers sometimes go the extra mile to check overload as well, it happens to gcc 4.4.3 and 4.8.2, but not 4.7.2 and 4.9.1.
Problem 2: As Marco A. pointed out, clang won't compile but gcc4.9 will still pass. However, gcc4.2 and gcc4.4 both reject the code, and the error they are complaining is "no matching function" rather than "calling non-static member without an object" in clang. There's seems to be no conclusive answer to this question, so I'm adding a language-lawyer tag as Daniel Frey suggested.
More Update:
I think for question 2 the answer is pretty clear now: it's up to the compiler whether adding static declaration will change the diagnosis. It varies from compiler to compiler and different versions of the same compiler. The language lawyer didn't show up, I'm going to accept Daniel Frey's answer as it best explained the first question. But answers from Marco A. and Hadi Brais also worth reading.
The standard only requires the name-lookup to happen at phase 1, when the template is first parsed. This turns up badfoo in badbar which is why the code compiles. The compiler is not required to do the overload resolution at that time.
The overload resolution (which always happens as a separate step after the name lookup) is then performed in phase 2 when badbar is instantiated - which is not the case in your example. This principle can be found in
3.4 Name lookup [basic.lookup]
1 The name lookup rules apply uniformly to all names (including typedef-names (7.1.3), namespace-names (7.3), and class-names (9.1)) wherever the grammar allows such names in the context discussed by a particular rule. Name lookup associates the use of a name with a declaration (3.1) of that name. Name lookup shall find an unambiguous declaration for the name (see 10.2). Name lookup may associate more than one declaration with a name if it finds the name to be a function name; the declarations are said to form a set of overloaded functions (13.1). Overload resolution (13.3) takes place after name lookup has succeeded. The access rules (Clause 11) are considered only once name lookup and function overload resolution (if applicable) have succeeded. Only after name lookup, function overload resolution (if applicable) and access checking have succeeded are the attributes introduced by the name’s declaration used further in expression processing (Clause 5).
(Emphasis mine)
I'd therefore say that the compiler(s) are correct to accept the code, although I'm not sure that they are required to do so.
To see the code being rejected, you need instantiate badbar.
Consider [temp.res]/8:
If no valid specialization can be generated for a template, and that
template is not instantiated, the template is ill-formed, no
diagnostic required.
This (in particular the "no diagnostic required" bit) makes any compiler's behaviour compliant with respect to worsebar. Implementations' discrepancies on this kind of code are just QoI issues - common compilers do some analysis and will complain. It is hard to say when exactly, and you should be prepared to come back to template code when upgrading or switching your implementation.
To make some clarity.. this version of the code compiles just fine on clang and gcc
class Base
{
public:
static void foo() {}
void badfoo(int a) {}
};
template <typename T>
class Derived : public Base
{
public:
void bar() { Base::foo(); }
void badbar() { Base::badfoo(); }
};
since
[temp.res]/p8
If no valid specialization can
be generated for a template, and that template is not instantiated, the template is ill-formed, no diagnostic
required.
Both gcc and clang aren't required to diagnose this. This one also falls in the same case as above (clang emits an error, gcc doesn't)
class Base
{
public:
static void foo() {}
void badfoo(int a) {}
};
template <typename T>
class Derived : public Base
{
public:
void bar() { Base::foo(); }
static void badbar() { Base::badfoo(); }
};
The case with
void worsebar() { Base::nonexist(); }
is different since it violates name lookup [temp.res]/p9
When looking for the declaration of a name used in a template definition, the usual lookup rules (3.4.1,
3.4.2) are used for non-dependent names
and [temp.res]/p10
If a name does not depend on a template-parameter (as defined in 14.6.2), a declaration (or set of declarations)
for that name shall be in scope at the point where the name appears in the template definition
Disclaimer: all of the above does not apply to MSVC which happily postpones all this stuff to the second phase of the lookup.
Edit:
in the case
class Base
{
public:
static void foo() {}
void badfoo(int i) {}
};
template <typename T>
class Derived : public Base
{
public:
static void badbar() { Base::badfoo(); } // static function
clang triggers an error while gcc doesn't. This falls in the first case since name lookup is successful but clang performs an additional check: since badfoo is a member function it tries to construct a valid implicit member reference expression. It then catches the fact that a member function is implicitly being called from a static function and detects the context mismatch. This diagnostic is entirely up to the compiler at this point (it wouldn't in case of an instantiation).
Before it instantiates any types or emits any code, the compiler incrementally builds a table of all symbols that have been declared. If an undeclared symbol has been used, it emits an error. That's why worsebar won't compile. On the other hand, badfoo has been declared and so badbar compiles. At this early point in the compilation process, the compiler won't check whether the call to badfoo actually matches the declared badfoo.
Since the Derived type has not been instantiated anywhere in the code, the compiler will not emit any code regarding it. In particular, badbar will just be neglected.
Now when you declare an instance of Derived (such as Derived< int >) but without using any of its members, the compiler will just create a type with those members that have been used and omit the others. Still, no error regarding badbar.
However, when declaring an instance of Derived and calling badbar, an instantiation of the badbar method would be required and so the compiler will create a type with badbar and compile it. This time, the compiler notices that badfoo is not actually declared and therefore emits an error.
This behavior is documented in the C++ standard in section 14.7.1.
Unless a member of a class template or a member template has been explicitly instantiated or explicitly specialized, the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist.
Finally, if badbar was static and was instantiated by the compiler (because it has been used) then the compiler will emit an error that badfoo does not exist. Now if you pass an integer argument to badfoo, another error will be emitted indicating that a static method cannot access an instance member because there is no instance in the first place.
Edit
The compiler is not obliged to NOT report semantic errors in uninstantiated template types. The standard just says that it does not have to, but it can. Regarding where to draw the line is open for debate. See this discussion about a related issue in clang:
which uninstantiated templates do we analyze? For performance reasons, I don't think we should analyze all the uninstantiated templates, as we may find ourselves repeatedly analyzing a huge portion of the Boost and the STL, etc.
So uninstantiated templates analysis changes with different versions of clang and gcc in different ways. But again, as per the standard: There's no requirement to report errors in uninstantiated templates, of course.
At first if you would instantiate Devired and try to call badbar there would be a compilation error:
// ...
int main()
{
Derived<int> d;
d.badbar();
return 0;
}
produces
error: too few arguments to function call, single argument 'i' was not specified
void badbar() { Base::badfoo(); } // compiles ok
~~~~~~~~~~~~ ^
The compiler doesn't compile the code, that is not instantiated.