libc++ is_copy_constructible seems wrong to me

c++, c++11, libc++, type-traits

Solution

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 as `is_constructible<T, const T&>::value`, otherwise `false`.

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`.

Problem

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.

Original source