Error when checking if a tuple of references is default constructible - c++

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.

Related

Eligible special member functions and triviality

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.

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.

Initializer "sizeof(T)" of inline static auto... Does it need instantiation?

What should happen if an expression's type is not dependent, but we use it to initialize a static auto variable? GCC and Clang differ in their behavior
template<typename T>
struct A {
static inline auto x = sizeof(T{}.f);
};
A<int> a;
GCC doesn't raise an error. But Clang thinks that this is invalid because it instantiates the operand of "sizeof". GCC appears to skip that step because sizeof(T{}.f) always has type size_t (not type dependent), so it already knows type of x without instantiation. Both compilers conformly reject the program if we refer to x, for example by (void) a.x;.
Does it even have to resolve the type of x at all? With C++14 upwards the language allows keeping things (like functions) with a "placeholder type" and doing delayed instantiation to find out about the actual return type later on, if I remember correctly. Does it have to apply this to x aswell, so keeping x with a placeholder type till we refer to a.x?
What compiler is correct according to the Standards?
EDIT
Someone asked
uhm, shouldnt' this be equivalent to this ?
template<typename T>
struct A {
static const std::size_t x;
};
template<typename T>
inline constexpr std::size_t A<T>::x = sizeof(T{}.f);
The difference, and what concerns me in my question, is that the static data member in my question is auto. Therefore, in order to know the type of x, you need to know the type of the initializer. Clang appears to instantiate the initializer eagerly in order to get the type. But GCC doesn't, apparently? I would like to understand what's going on.
From [temp.inst]/3:
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; in particular, the initialization (and any associated side effects) of a static data member does not occur unless the static data member is itself used in a way that requires the definition of the static data member to exist.
Simply writing A<int> a; does noes not use A<int>::x in a way that requires its definition to exist, so its initialization should not occur. gcc is correct.

When are template non-static data member initialisers instantiated?

Here is a short self-contained test case to explain my question. GCC accepts this code, but clang and Intel reject it:
template <typename T>
struct false_t {
static const bool value = false;
};
template <typename T>
int f() {
static_assert(false_t<T>::value, "");
return 0;
}
template <typename T>
struct S {
int m = f<T>();
};
int s = sizeof(S<int>);
Or, based on pmr's comment, here is a simpler example which too is accepted by gcc and rejected by clang:
struct S;
template <typename T> struct X { int x = T(); };
int s = sizeof(X<S>);
sizeof(S<int>) (or sizeof(X<S>)) is supposed to instantiate the bits of the class it needs, but the compilers disagree on which bits those are. Since a non-static data member initializer would only be used by a constructor, GCC performs the instantiation as part of instantiating the class's constructor. clang and Intel do so earlier.
I'm having trouble understanding what the standard says in [temp.inst]p1:
The implicit instantiation of a class template specialization causes the implicit
instantiation of the declarations, but not of the definitions or default arguments, of the class member functions, member classes, scoped member enumerations, static data members and member templates; and it causes the implicit instantiation of the definitions of unscoped member enumerations and member anonymous unions.
because I don't see where even the declarations of the non-static data members (with or without initialisers) get instantiated at all.
A bit more details about where I encountered this: I was trying to create a template helper class (that would never be created at runtime) containing a member initialised using std::declval<T>() (which, I should add, I now realise wouldn't be of much use anyway), and libstdc++'s implementation of std::declval contains a static assertion like the one in my example. I can work around the problem without much effort by avoiding std::declval, but I would like to know what the standard requires.
While attempting to figure out how to ask this question, I stumbled upon the answer, but thought it might be useful to post anyway.
This is one of the open issues of the C++ standard, issue 1396 to be precise. The intent is that the initialisers only get instantiated as needed:
Non-static data member initializers get the same late parsing as member functions and default arguments, but are they also instantiated as needed like them? And when is their validity checked?
Notes from the October, 2012 meeting:
CWG agreed that non-static data member initializers should be handled like default arguments.
but there are quite a number problems with that approach that are still being resolved. Until they are resolved, it's only natural that different compilers perform the instantiation at different times, and code that requires specific behaviour should be rewritten to avoid such a requirement. In my case, that means not using std::declval.

Do non-dependent default template arguments of function templates allow for SFINAE?

With "non-dependent" here I mean "non-dependent on any other template arguments of that specific function template".
While answering this question, I thought I found the answer, but according to #Johannes (in the comments to my answer), I'm misinterpreting the standard here. Take the following simple example:
#include <type_traits>
template<class T>
struct X{
template<class U = typename T::type>
static void foo(int){}
static void foo(...){}
};
int main(){
X<std::enable_if<false>>::foo(0);
}
(Live version.)
Is there any guarantee that the above compiles? GCC and Clang disagree here, as can be seen in the live version when switching between them. Interestingly, though, the following is accepted by GCC:
#include <type_traits>
template<class T>
struct X{
template<bool = T::f()>
static void foo(int){}
static void foo(...){}
};
struct Y{
static bool f(){ return true; }
};
int main(){
X<Y>::foo(0);
}
(Live version.)
The second snippet will only print foo(int) if T contains a constexpr static function f. Again, interestingly, if you completely remove f from Y (or pass, say, int instead), GCC complains about a missing member, indicating that it doesn't allow for SFINAE - which is contradictory with the previous observation. Clang takes all variations and applies SFINAE, and I wonder if that's what is guaranteed by the standard.
(FWIW, MSVC with the Nov CTP generally agrees with Clang, but crashes on the second snippet if the function is present, likely because they don't have constexpr. I submitted a bug report here.)
I think the code in question is incorrect, as when the class template is instantiated, all member declarations are instantiated, except the definition parts and default arguments of the member functions and member function templates. The Standard also defines when the function default arguments are instantiated precisely.
So default template-arguments are immediately instantiated. The possibility that default arguments could be intended to include default template arguments at this point is very small here in my opinion, because there is no description of when such an argument would be instantiated later.
This is in line with the requirement that "A default template-argument shall not be specified in the template-parameter-lists of the definition of a member of a class template that appears outside of the member's class.", since there would be no way that such a template argument be instantiated immediately when instantiating the surrounding class template.