Clang and GCC appear not to honor friend declarations when evaluating std::is_constructible and std::is_destructible.
Regarding `is_constructible, cppreference.com says:
Access checks are performed as if from a context unrelated to T and any of the types in Args. Only the validity of the immediate context of the variable definition is considered.
(The site doesn't explain how is_destructible deals with access checks, but access modifiers do affect the behavior of is_destructible in general, so I'd expect it to work the same way as is_constructible.)
Therefore, it seems to me that this code should not compile, since in the immediate context of the check the constructor and destructor are available, as evidenced by the local variable instantiation:
class Private
{
Private() {}
~Private() {}
friend class Friend;
};
class Friend
{
public:
Friend()
{
// Both of these should fire, but they do not.
static_assert(
!std::is_constructible<Private>::value,
"the constructor is public");
static_assert(
!std::is_destructible<Private>::value,
"the destructor is public");
// There is no error here.
Private p;
}
};
...but Coliru compiles it without error (using either GCC or Clang).
Is this a bug (or at least a nonconformity) in both compilers, or is cppreference.com misrepresenting the standard, or am I misunderstanding cppreference.com's statement?
This is exactly what
Access checks are performed as if from a context unrelated to T and
any of the types in Args.
says. "A friend of T" is by definition not "unrelated to T".
"immediate context" is a term of art, but in any event the sentence is talking about the immediate context of the hypothetical variable definition, not the use of is_constructible.
It would be madness to make the is_constructible check context-dependent; that would mean that the same type, is_constructible<T, Args...>, has different base classes in different contexts.
Related
Consider the following code:
#include <type_traits>
template<typename T>
concept Int = std::is_same_v<T, int>;
template<typename T>
concept Float = std::is_same_v<T, float>;
template<typename T>
struct Foo
{
Foo() requires Int<T> = default; // #1
Foo() requires Int<T> || Float<T> = default; // #2
};
static_assert(std::is_trivial_v<Foo<float>>);
For static assert to be satisfied, Foo<float> needs to be a trivial type, so, since it is a class type, be a trivial class. Thus, apart from being trivially copyable (which it is), it has to have an eligible default constructor (and all such constructors have to be trivial, class.prop#2), which is a special case of eligible special member function, defined by special#6:
An eligible special member function is a special member function for which:
the function is not deleted,
the associated constraints ([temp.constr]), if any, are satisfied, and
no special member function of the same kind is more constrained ([temp.constr.order]).
Rigorously, Foo<float> does not have an eligible default constructor, because constraints of #1 are unsatisfied and for #2 there's a more constrained default constructor (#1). Thus, Foo<float> is not a trivial class. However, the code above compiles successfully on gcc, clang and msvc (godbolt). Intuitively, it probably should, but then, it seems, the third clause of the definition above should be
no special member function of the same kind with satisfied associated constraints (if any) is more constrained ([temp.constr.order])
to allow #2 not to be "shadowed" by #1 (which would never be chosen during overload resolution for default constructor of Foo<float> anyway) and become eligible, making (due to being trivial and only one eligible) Foo<float> trivial.
So, is my analysis correct? If so, is this a compiler bug or the wording of the aforementioned clause is imprecise and it actually implies my version of it?
So, is my analysis correct?
Definitely. My intent in writing this wording in P0848 was very much that #2 is eligible - that's the one overload resolution would pick and it's not deleted, so it should be eligible.
I opened a CWG issue request which is now CWG 2595.
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.
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).
With g++-5 I get the following output
#include <type_traits>
#include <tuple>
int main()
{
bool b;
b = std::is_default_constructible<int>::value; //Compiles, returns true
b = std::is_default_constructible<int&>::value; //Compiles, returns false
b = std::is_default_constructible< std::tuple<int> >::value; //Compiles, returns true
b = std::is_default_constructible< std::tuple<int&> >::value; //Does not compile
}
Is this a bug in is_default_constructible's implementation ?
The error message is a long stack list ending in:
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.1.0/../../../../include/c++/5.1.0/tuple:105:9: error: reference to type 'int' requires an initializer
: _M_head_impl() { }
This is not a bug in is_default_constructible. That type trait is only required to check the immediate context of default construction, it doesn't have to deeply evaluate any member initializers. This restriction is probably so that it can be implemented without dedicated compiler magic by using SFINAE. (see [meta.unary.prop], esp. p7).
The tuple and pair default constructors were not required to fail in the immediate context (SFINAE-friendly) if the element type can't be default-constructed. This has been addressed by LWG 2367, which introduces the following SFINAE requirement for the tuple default constructor:
Remarks: This constructor shall not participate in overload resolution
unless is_default_constructible<Ti>::value is true for all i. [...]
With this additional requirement, default construction of a tuple must fail in a SFINAE-friendly way, such that is_default_constructible now works for tuple if the elements fail to be default-constructed in the immediate context (which is the case for reference types).
LWG 2367 is currently in Ready status; the proposed resolution has not (yet) been incorporated into the github drafts.
[-- this part is still under consideration
Yakk raised an important point in the comments: Why does is_default_constructible have to deeply instantiate the member initializers?
As far as I can tell, this has to do with the conditional constexpr'iveness of tuple's default constructor. is_default_constructible causes the instantiation of the default constructor. It only needs to instantiate the declaration in order to determine whether or not this constructor can be called without failures in the immediate context. However, the instantiation of the declaration requires determining the constexpr'iveness, and this causes the instantiation of the definition of the constructor.
A member function (or constructor) of a class template which has been marked as constexpr is only conditionally constexpr: only the member functions of those class template instantiations will be constexpr where the body doesn't violate the constexpr restrictions. This requires the instantiation of the body of the constructor, for constructors in order to check if the member initializers are allowed inside a constexpr function. Consider:
struct nonconstexpr { nonconstexpr() { std::cout << "runtime\n"; } };
struct isconstexpr { constexpr isconstexpr() {} };
template<typename T>
struct wrapper { T t; constexpr wrapper() : t() {} };
When instantiating the default ctor of wrapper, the compiler has to instantiate the member initializers in order to determine whether or not this instantiation shall be constexpr.
In the case of std::tuple, this causes somewhere the instantiation of a member-initializer which tries to value-initialize the reference tuple leaf (data member). This is an error, and it does not occur within the immediate context of the original instantiation of the default constructor. Therefore, it is a hard error rather than a Substitution Failure in the immediate context.
--]
This part isn't entirely clear to me because CWG 1358 essentially made all instantiations constexpr, whether or not they actually satisfy the criteria. And indeed, gcc 6.0 does not fail to compile the following example, while gcc 5.1 and clang 3.7 reject it:
#include <type_traits>
template<typename T>
struct foo
{
T t;
constexpr foo() {} // remove `constexpr` to make it compile everywhere
};
int main()
{
static_assert(std::is_default_constructible<foo<int&>>{}, "!");
}
CWG 1358 also tells us why the distinction between the two approaches - conditional constexpr and constexpr despite violations - is important:
Questions arose in the discussion of issue 1581 as to whether this
approach — making the specialization of a constexpr function template
or member function of a class template still constexpr but unable to
be invoked in a constant context — is correct. The implication is that
class types might be categorized as literal but not be able to be
instantiated at compile time. This issue is therefore returned to
"review" status to allow further consideration of this question.
For libc++, there is bug #21157, which has been resolved on 2014-10-15 and appears in the clang3.6 branch. For libstdc++, there doesn't seem to be a bug report; the issue was fixed in a combined commit on 2015-06-30 which also implements N4387 - Improving Pair and Tuple (Revision 3) which currently does not seem to appear in any gcc5 branches.
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.