7

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() { }
Deduplicator
  • 44,692
  • 7
  • 66
  • 118
user3559888
  • 1,157
  • 1
  • 9
  • 12
  • What is the error message? – Maxim Egorushkin Jul 15 '15 at 14:58
  • Edited it in @MaximEgorushkin :-) – user3559888 Jul 15 '15 at 15:04
  • I'm wondering how the body of a constructor got evaluated in that case, but not in a simple `templatestruct test{T t; test():t();};` case. Interesting. – Yakk - Adam Nevraumont Jul 15 '15 at 15:51
  • Could be related to how SFINAE works. This is not the test function template that fails to instantiate, rather instantiating another template to instantiate the test function template... – Maxim Egorushkin Jul 15 '15 at 16:48
  • Compiled fine on Visual C++ 2015 and gave the value false as expected. – MS Srikkanth Jul 15 '15 at 17:14
  • Works fine in gcc 6.0. – dyp Jul 15 '15 at 17:19
  • This is [LWG 2367](http://cplusplus.github.io/LWG/lwg-active.html#2367), which corresponds to [libc++ bug #21157](https://llvm.org/bugs/show_bug.cgi?id=21157). I bet there's a also a related libstdc++ bug, since it also works in recent versions there. – dyp Jul 15 '15 at 17:29
  • Sorry I think my clang actually used libc++, I've edited it out. – user3559888 Jul 15 '15 at 17:35
  • 1
    In libstdc++ this was fixed in a single commit that also implemented the improvements for pair and tuple of N4387: https://github.com/gcc-mirror/gcc/commit/d3c64041b32b6962ad6b2d879231537a477631fb I can't find a bug report that mentions LWG 2367. – dyp Jul 15 '15 at 17:37

1 Answers1

8

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.

Community
  • 1
  • 1
dyp
  • 38,334
  • 13
  • 112
  • 177
  • The thing is, `is_default_constructible<>` usually doesn't cause deep failures to be exposed. In this case, it is. Something to do with the `tuple` implementation. – Yakk - Adam Nevraumont Jul 16 '15 at 16:30
  • 1
    @Yakk It seems it has to do with the conditional constexpr'iveness o.O http://coliru.stacked-crooked.com/a/59356c32a3c764c8 – dyp Jul 16 '15 at 16:53
  • So that is a QOI issue, in that there technically is no need to determine if the expression is `constexpr` there. – Yakk - Adam Nevraumont Jul 16 '15 at 17:25