I've run into a rather troubling situation in which I'm trying to use std::make_any with a class I've written which accepts std::any in one of its constructors, but in attempting to come up with a minimal test case, have come across what I think is probably a bug in the implementation of std::any and/or std::is_copy_constructible, however I'm not 100% sure. Here are a few minimal examples which variously work or don't work using g++8 and clang++9. In each example, all the same lines of code are present, but in 4 states of two lines being commented-out or not, and the result.
I'm using the C++17 standard, passing the -std=c++17
flag to both g++-8 and clang++-9, and they're both using libstdc++ version 8.3.0 (Ubuntu 18.04).
The following compiles using both g++-8 and clang++-9.
#include <any>
#include <type_traits>
struct Hippo {
Hippo () { }
Hippo (Hippo const &) { }
Hippo (Hippo &&) { }
// Hippo (std::any) { }
};
int main (int argc, char **argv) {
static_assert(std::is_copy_constructible_v<Hippo>);
// auto w = std::make_any<Hippo>();
return 0;
}
The following also works for both, as one would expect.
#include <any>
#include <type_traits>
struct Hippo {
Hippo () { }
Hippo (Hippo const &) { }
Hippo (Hippo &&) { }
// Hippo (std::any) { }
};
int main (int argc, char **argv) {
static_assert(std::is_copy_constructible_v<Hippo>);
auto w = std::make_any<Hippo>();
return 0;
}
The following compiles with g++-8 but fails with clang++-9. This one is confusing, because the value of std::is_copy_constructible_v<Hippo>
shouldn't change by adding a constructor unrelated to copy construction.
#include <any>
#include <type_traits>
struct Hippo {
Hippo () { }
Hippo (Hippo const &) { }
Hippo (Hippo &&) { }
Hippo (std::any) { }
};
int main (int argc, char **argv) {
static_assert(std::is_copy_constructible_v<Hippo>);
// auto w = std::make_any<Hippo>();
return 0;
}
The error given by clang++-9 is (clipped for length):
/usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/type_traits:132:31: error: no member named 'value' in 'std::is_copy_constructible<Hippo>'
: public conditional<_B1::value, _B2, _B1>::type
~~~~~^
/usr/bin/../lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/any:170:17: note: in instantiation of template class 'std::__and_<std::is_copy_constructible<Hippo>, std::is_constructible<Hippo, const Hippo &> >' requested here
enable_if<__and_<is_copy_constructible<_Tp>,
Here, it looks like the std::is_copy_constructible<Hippo>
type is incomplete at the point of use (within the <any>
header), but it should have a bool-valued constexpr value
member. This is what I think is a bug, since g++8 compiles this just fine. Presumably the bug is in libstdc++'s implementation of std::any and/or std::is_copy_constructible, but because g++-8 works and clang++-9 fails, this is confusing.
However, the following fails to compile in both g++-8 and clang++-9:
#include <any>
#include <type_traits>
struct Hippo {
Hippo () { }
Hippo (Hippo const &) { }
Hippo (Hippo &&) { }
Hippo (std::any) { }
};
int main (int argc, char **argv) {
static_assert(std::is_copy_constructible_v<Hippo>);
auto w = std::make_any<Hippo>();
return 0;
}
Unsurprisingly the error produced by clang++-9 is the same, but the error from g++-8 claims that all the candidates for template argument deduction fail for std::any's constructor, including the one that should work, but buried in that constructor is a usage of std::is_copy_constructible, so I think that's the same problem popping up, and I'm pretty sure it's a bug. I've cut out the irrelevant parts of the error message:
/usr/include/c++/8/any: In instantiation of ‘std::any std::make_any(_Args&& ...) [with _Tp = Hippo; _Args = {}]’:
.../any.cpp:13:35: required from here
/usr/include/c++/8/any:431:14: error: no matching function for call to ‘std::any::any(const std::in_place_type_t<Hippo>&)’
return any(in_place_type<_Tp>, std::forward<_Args>(__args)...);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<...cut for length...>
/usr/include/c++/8/any:208:7: note: candidate: ‘template<class _ValueType, class ... _Args, class _Tp, class _Mgr, typename std::enable_if<std::__and_<std::is_copy_constructible<_Tp>, std::is_constructible<_Tp, _Args&& ...> >::value, bool>::type <anonymous> > std::any::any(std::in_place_type_t<_Tp>, _Args&& ...)’
any(in_place_type_t<_ValueType>, _Args&&... __args)
^~~
/usr/include/c++/8/any:208:7: note: template argument deduction/substitution failed:
<...cut for length...>
Anyway, my question stands -- this is a bug in libstdc++, right?
In fully researching this question, I tried compiling against libc++ (using clang++-9 only) instead of libstdc++, and that solved the problem. Thus it would appear that it is a legit bug in libstdc++ 8.3.0.