Подтвердить что ты не робот

LibС++ is_copy_constructible кажется мне неправильным

Реализация lib_ ++ is_copy_constructible выглядит так:

template <class _Tp>
struct _LIBCPP_TYPE_VIS_ONLY is_copy_constructible
    : public is_constructible<_Tp, const typename add_lvalue_reference<_Tp>::type>
    {};

Спецификация С++ для is_copy_constructible проста:

std::is_copy_constructible specification: std::is_constructible<T, const T&>::value is true.

Однако не реализация выше реализации T & const вместо const T &? Применение const к add_lvalue_reference не должно иметь никакого эффекта, и по крайней мере один компилятор (EDG) распознает это в виде предупреждения.

Пример программы, демонстрирующей проблему:

#include <type_traits>

struct ProofTest
{
    ProofTest(){}
    ProofTest(const ProofTest&) = delete;  // is_copy_constructible should use this.
    ProofTest(ProofTest&){ }               // But instead it using this.
};

void Proof()
{
    static_assert(std::is_copy_constructible<ProofTest>::value == false, "is_copy_constructible bug");
}

В libstdС++ приведенный выше код компилируется ОК, но под libС++ запускается static_assert.

Является следующее правильное исправление?:

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>
    {};

Это также влияет на некоторые другие черты типа libС++.

4b9b3361

Ответ 1

Согласен, спасибо за отчет об ошибке.

Обновить

Связанный вопрос: Какое ожидаемое значение: std::is_constructible<int&>::value? Это не совсем понятно для мне от чтения стандарта.

Что говорится в стандарте:

Для ссылочного типа T, тот же результат, что и is_constructible<T, const T&>::value, иначе false.

A "ссылочный тип" - это в основном ничего, кроме void. Я перефразирую. Это не точное определение. Он должен быть понятным, а не точным. Языковой адвокат (включая меня) может разорвать его на части. Но для удобства понимания "все, кроме a void" достаточно близко.

Итак, ваш вопрос становится, что есть:

std::is_constructible<int&, const (int&)&>::value  // I've used pseudo code

const, примененный к ссылкам, является no-op(). И ссылки lvalue, применяемые к lvalue-ссылкам, являются no-op (из-за списания ссылок). Например, рассмотрите этот не переносимый объект type_name:

#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';
}

Для меня это печатает:

int&

Таким образом, приведенное выше упрощает:

std::is_constructible<int&, int&>::value  // true

который должен отвечать true, поскольку lvalue int должно быть конструктивным из неконстантного lvalue int.