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() { }
A default constructible class is a class that has a default constructor (either its implicit constructor or a custom defined one). The is_default_constructible class inherits from integral_constant as being either true_type or false_type, depending on whether T is default constructible.
A class does so by defining a special constructor, known as the default constructor. This constructor is called the default constructor because it is run "by default;" if there is no initializer, then this constructor is used. The default constructor is used regardless of where a variable is defined.
base a declares a variable a of type base and calls its default constructor (assuming it's not a builtin type). base a(); declares a function a that takes no parameters and returns type base .
A trivially default constructible type is a type which can be trivially constructed without arguments or initialization values, either cv-qualified or not. This includes scalar types, trivially default constructible classes and arrays of such types.
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 alli
. [...]
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.
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