Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error when checking if a tuple of references is default constructible

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() { }
like image 862
user3559888 Avatar asked Jul 15 '15 14:07

user3559888


People also ask

What is default constructible C++?

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.

Is default constructor always called c++?

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.

How to invoke default constructor in c++?

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 .

Is trivially default constructible?

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.


1 Answers

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.

like image 150
dyp Avatar answered Sep 27 '22 23:09

dyp