Std:: is_constructible по типу с непубличным деструктором

Каков ожидаемый результат для std::is_constructible для типа с закрытым или защищенным деструктором?

Например, я все еще могу построить такой объект в куче, даже если только друг может его освободить:

#include <type_traits>

class Foo
{
    friend void freeFoo(Foo*);
public:
    Foo()
    {}
private:
    // Destructor is private!
    ~Foo()
    {}
};

void freeFoo(Foo* f)
{
    delete f;  // deleting a foo is fine here because of friendship
}

int main()
{
    Foo* f = new Foo();
    // delete f;   // won't compile: ~Foo is private
    freeFoo(f);    // fine because of friendship


    if(!std::is_constructible<Foo>::value)
    {
        std::cout << "is_constructible failed" << std::endl;
    }
}

Окончательная проверка для is_constructible завершится неудачей как на gcc, так и на Visual С++ (gcc demo on coliru).

Это обязательное поведение по стандарту? Если да, есть ли способ проверить, имеет ли тип определенный конструктор, независимо от спецификатора доступа на деструкторе?

Ответ 1

С++ 14 FD определяет is_constructible следующим образом:

Учитывая следующее объявление функции:

template <class T>
add_rvalue_reference_t<T> create() noexcept;

условие предиката для типовой специализации is_constructible<T, Args...> должно выполняться тогда и только тогда, когда следующее определение переменной будет хорошо сформировано для некоторых изобретенных переменная t:

T t(create<Args>()...);

Проверка доступа выполняется как в контексте, не связанном с tи любой из Args. Только справедливость контекста рассматривается переменная инициализация. [Примечание: оценка инициализации может привести к побочным эффектам, таким как создание шаблонов шаблонов шаблонов и шаблонов функций специализация, генерация неявно определенных функций и скоро. Такие побочные эффекты не находятся в "непосредственном контексте" и могут приводят к плохой форме программы. -end note]

Теперь вопрос существенно сводится к "Является ли вызов деструктора в непосредственном контексте инициализации переменной?" [Class.dtor]/11:

Деструктор вызывается неявно

  • для построенного объекта со статической продолжительностью хранения (3.7.1) при завершении программы (3.6.3),
  • для построенного объекта с автоматической продолжительностью хранения (3.7.3), когда блок, в котором создается объект, завершает (6.7),
  • для созданного временного объекта, когда его время жизни заканчивается (12.2).

В каждом случае контекст вызова - это контекст построение объекта.

Таким образом, вызов деструктора находится в контексте конструкции (которая, по-видимому, является синонимом инициализации здесь), что подразумевает, что она рассматривается и заставляет черту возвращаться false.
Я полагаю, что это недоказано (например, немедленный или неявно-непосредственный контекст?), Но интуитивно я ожидаю, что соответствующая реализация будет отмечать выражение NotDestructible() как плохо сформированное - либо SFINAE-дружественное, либо нет (желательно первое). Тем не менее, он не был хорошо сформирован. Clang с libС++, libstdС++ и GCC говорят, что он недействителен, SFINAE-friendly.


Если да, есть ли способ проверить, имеет ли тип конкретный конструктор, независимо от спецификатора доступа на деструкторе?

Как насчет использования new?

template <typename T, typename... Args>
class is_only_constructible
{
    template <typename, typename=void> struct test : std::false_type {};
    template <typename U>
    struct test<U, decltype(void(new U(std::declval<Args>()...)))> : std::true_type {};

public:
    static constexpr bool value = test<T>::value;
};

Демо. Согласованные черты могут быть легко установлены: возьмите признак is_only_constructible и объедините его с is_destructible (очевидно, последний возвращает false в сочетании с частными деструкторами).

Ответ 2

Указание параграфа [meta.unary.prop]/7 стандарта С++ (проект N4296):

Учитывая следующее объявление функции:

template <class T>
add_rvalue_reference_t<T> create() noexcept;

условие предиката для специализированной специализации is_constructible<T, Args...> должно выполняться тогда и только тогда, когда следующее определение переменной будет хорошо сформировано для некоторой изобретенной переменной t:

T t(create<Args>()...);

Другими словами, is_constructible<T, Args...>::value дает false, если деструктор недоступен.