【问题标题】:std::is_constructible on type with non-public destructor具有非公共析构函数的类型的 std::is_constructible
【发布时间】:2015-01-22 10:01:20
【问题描述】:

对于具有私有或受保护析构函数的类型,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;
    }
}

在 gcc 和 Visual C++ (gcc demo on coliru) 上对 is_constructible 的最终检查将失败。

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

【问题讨论】:

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


【解决方案1】:

C++14 FD 对is_constructible 的定义如下:

给定以下函数声明:

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

模板特化的谓词条件 当且仅当满足is_constructible&lt;T, Args...&gt; 对于某些发明,以下变量定义将是格式良好的 变量t:

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

访问检查如同在与T 无关的上下文中执行 和任何Args。只有直接上下文的有效性 考虑变量初始化。 [ 注意:评估 初始化可能会导致副作用,例如 类模板特化和函数模板的实例化 专业化,隐式定义函数的生成,以及 很快。此类副作用不在“直接上下文”中,并且可以 导致程序格式错误。 ——尾注 ]

现在问题基本上归结为“在变量初始化的直接上下文中调用析构函数吗?” [class.dtor]/11:

隐式调用析构函数

  • 对于在程序终止 (3.6.3) 时具有静态存储持续时间 (3.7.1) 的构造对象,
  • 对于在其中创建对象的块退出 (6.7) 时具有自动存储持续时间 (3.7.3) 的构造对象,
  • 在其生命周期结束时构造的临时对象 (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)。

【讨论】:

  • 我想知道:如果委员会的意图是让is_constructible&lt;T, Args...&gt;::value 产生true 即使T 的析构函数不可访问(这似乎是你所声称的),为什么没有根据 new 表达式指定该特征的行为?
  • @AndyProwl 这不是我的意思。我认为委员会没有正确考虑这个案例,因此文本没有明确说明。
  • 我明白了。当时我可能误解了这句话:“据我所知,析构函数调用不在初始化的直接上下文中,因此不应考虑”。
  • @AndyProwl 我不确定意图。一个 EWG 问题也许是合适的,你不觉得吗?
  • @Columbo:也许吧,虽然我已经习惯了标准的晦涩难懂,以至于我有点停止推理实际措辞的含义,而是开始考虑委员会的意图更多可能在写的时候。最终结果是一样的(“我不知道,我可能错了”),但至少我不会从一个模棱两可的段落跳到另一个段落,从而节省了一些神经寿命:)
【解决方案2】:

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

给定以下函数声明:

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

模板特化is_constructible&lt;T, Args...&gt; 的谓词条件当且仅当以下变量定义对于某个发明变量t 的格式正确:

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

换句话说,如果析构函数不可访问,is_constructible&lt;T, Args...&gt;::value 将产生 false

【讨论】:

  • 那么问题的第二部分呢:有没有办法检查该类型是否具有特定的构造函数,而不管析构函数上的访问说明符如何?
  • @ComicSansMS:恐怕我不能回答那部分(至少,不是从我的头顶),虽然我怀疑答案是“不”。
  • 这就是我害怕的。检查普通成员函数的存在并不难,但构造函数很棘手。我想这可能被认为是type_traits 的疏忽。
  • @ComicSansMS 我的回答中有一个可能的解决方法。
  • @ComicSansMS:我不知道为什么,但出于某种原因,我一直在考虑检测私有/受保护的构造函数,而问题显然是析构函数的访问说明符。哥伦布的解决方案确实是我应该想到的。
猜你喜欢
  • 2020-11-11
  • 2014-05-18
  • 1970-01-01
  • 2019-09-13
  • 1970-01-01
  • 2011-03-04
  • 1970-01-01
  • 2020-03-18
  • 1970-01-01
相关资源
最近更新 更多