The libc++ implementation of is_copy_constructible is like so:
template <class _Tp>
struct _LIBCPP_TYPE_VIS_ONLY is_copy_constructible
: public is_constructible<_Tp, const typename add_lvalue_reference<_Tp>::type>
{};
The C++ specification for is_copy_constructible is simply:
std::is_copy_constructible specification: std::is_constructible<T, const T&>::value is true.
However, isn't the implementation above implementing T& const instead of const T&? Applying const to add_lvalue_reference should have no effect, and at least one compiler (EDG) recognizes this in the form of a warning.
Example program demonstrating the problem:
#include <type_traits>
struct ProofTest
{
ProofTest(){}
ProofTest(const ProofTest&) = delete; // is_copy_constructible should use this.
ProofTest(ProofTest&){ } // But instead it's using this.
};
void Proof()
{
static_assert(std::is_copy_constructible<ProofTest>::value == false, "is_copy_constructible bug");
}
Under libstdc++ the above code compiles OK, but under libc++ the static_assert fires.
Is the following the correct fix?:
template <class _Tp>
struct _LIBCPP_TYPE_VIS_ONLY is_copy_constructible
: public is_constructible<_Tp, typename add_lvalue_reference<typename std::add_const<_Tp>::type>::type>
{};
This affects a couple other libc++ type traits as well.
Agreed, thanks for the bug report.
Update
Related question: What's the expected value of:
std::is_constructible<int&>::value
? It's not perfectly clear to me from reading the standard.
What the standard says:
For a referenceable type
T
, the same result asis_constructible<T, const T&>::value
, otherwisefalse
.
A "referenceable type" is basically anything but a void
. I'm paraphrasing. This is not an exact definition. It is meant to be understandable as opposed to precise. A language lawyer (including myself) can tear it apart. But for ease in understanding, "anything but a void
" is close enough.
So your question becomes, what is:
std::is_constructible<int&, const (int&)&>::value // I've used pseudo code
const
applied to references is a no-op (). And lvalue references applied to lvalue references is a no-op (due to reference collapsing). For example consider this non-portable type_name
facility:
#include <type_traits>
#include <memory>
#include <iostream>
#include <cxxabi.h>
#include <cstdlib>
template <typename T>
std::string
type_name()
{
typedef typename std::remove_reference<T>::type TR;
std::unique_ptr<char, void(*)(void*)> own
(
abi::__cxa_demangle(typeid(TR).name(), nullptr,
nullptr, nullptr),
std::free
);
std::string r = own != nullptr ? own.get() : typeid(TR).name();
if (std::is_const<TR>::value)
r += " const";
if (std::is_volatile<TR>::value)
r += " volatile";
if (std::is_lvalue_reference<T>::value)
r += "&";
else if (std::is_rvalue_reference<T>::value)
r += "&&";
return r;
}
int
main()
{
typedef int& T;
std::cout << type_name<const T&>() << '\n';
}
For me this prints out:
int&
So the above simplifies to:
std::is_constructible<int&, int&>::value // true
which should answer true
since an lvalue int
should be constructible from a non-const lvalue int
.
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