具有非公共析构函数的std :: is_constructible类型

时间:2015-01-22 10:01:21

标签: c++ c++11 language-lawyer typetraits

对于具有私有或受保护的析构函数的类型,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 C ++(gcc demo on coliru)上失败。

这是标准所要求的行为吗?如果是这样,有没有办法检查类型是否具有特定的构造函数,而不管析构函数上的访问说明符是什么?

2 个答案:

答案 0 :(得分:13)

C ++ 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 ]

现在问题基本上减少到&#34;析构函数是否在变量初始化的直接上下文中调用?&#34; [class.dtor] / 11:

  

隐式调用析构函数

     
      
  • 表示在程序终止时具有静态存储持续时间(3.7.1)的构造对象(3.6.3),
  •   
  • 对于具有自动存储持续时间(3.7.3)的构造对象,当创建对象的块退出时(6.7),
  •   
  • 表示构造的临时对象的生命周期结束时(12.2)。
  •   
     

在每种情况下,调用的上下文都是上下文   构建对象。

因此析构函数调用位于构造的上下文中(这可能与此处的初始化同义),这意味着它被考虑并导致特征返回false
我认为这是不明确的(例如,即时与非显式直接上下文?),但直觉上我希望符合标准的实现将表达式NotDestructible()标记为格式错误 - 无论是SFINAE还是非友好(最好是前者) )。但是,从来没有良好的形式 Clang with libc++, libstdc++ and GCC do say that it's invalid, 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;
};

Demo。可以轻松建立一致的特征:获取is_only_constructible特征并将其与is_destructible结合使用(当与私有析构函数结合使用时,显然后者返回false。)

答案 1 :(得分:5)

引用C ++标准(草案N4296)的段落[meta.unary.prop] / 7:

  

给出以下函数声明:

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