题 具有非公共析构函数的类型的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 C ++上失败(关于coliru的gcc演示)。

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


11
2018-01-22 10:01


起源


有关: groups.google.com/a/isocpp.org/forum/#!topic/std-discussion/... - Caramiriel
@Caramiriel不幸的是,它没有直接解决这个问题。 is_destructible 显式取决于析构函数的可访问性,这不是这里的情况。 - Columbo


答案:


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。只有直接上下文的有效性   考虑变量初始化。 [ 注意:评价   初始化可能导致副作用,如   类模板特化和函数模板的实例化   专业化,隐式定义函数的生成,以及   等等。这种副作用不在“直接背景”中,并且可以   导致程序格式不正确。 - 注意 ]

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

隐式调用析构函数

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

在每种情况下,调用的上下文都是上下文   建造物体。

因此析构函数调用是在构造的上下文中(这可能与此处的初始化同义),这意味着它被考虑并导致特征返回 false
我认为这是不明确的(例如,即时与非显式直接上下文?),但直觉上我希望符合标准的实现标记表达式 NotDestructible() 形成不良 - 无论是SFINAE还是不友好(最好是前者)。但是,从来没有良好的形式。
用libc ++,libstdc ++和GCC来表示它是无效的,SFINAE友好


如果是这样,有没有办法检查类型是否具有特定类型   构造函数,无论析构函数上的访问说明符如何?

怎么样使用 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 当与私人析构者结合时)。


13
2018-01-22 11:12



我想知道:如果委员会的意图是 is_constructible<T, Args...>::value 产量 true 甚至在 T析构函数是不可访问的(这似乎是你所声称的),为什么不指定这个特征的行为? new 表达? - Andy Prowl
@AndyProwl这不是我的意思。我认为委员会没有适当考虑这个案例,因此案文不详。 - Columbo
我懂了。我可能误解了这句话:“据我所见,析构函数调用不是在初始化的直接上下文中,因此不应该被考虑”。 - Andy Prowl
@AndyProwl我不确定意图。 EWG问题也许是合适的,你不觉得吗? - Columbo
@Columbo:也许,虽然我已经习惯了标准的超级模糊,但我还是停止了对实际措辞所暗示的内容的推理,而是开始考虑委员会在撰写时的意图更有可能。最终结果是相同的(“我不知道,我可能是错的”),但至少我通过不从一个模糊的段落反弹到另一个来节省一些神经生命:) - Andy Prowl


引用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 如果析构函数不可访问。


5
2018-01-22 10:09



那么问题的第二部分呢:有没有办法检查类型是否具有特定的构造函数,而不管析构函数中的访问说明符是什么? - ComicSansMS
@ComicSansMS:我担心我不能回答那个部分(至少,不是从我的头脑中回答),尽管我怀疑答案是“不”。 - Andy Prowl
这就是我所害怕的。检查普通成员函数的存在并不难,但构造函数很棘手。我想这可能被认为是一种疏忽 type_traits。 - ComicSansMS
@ComicSansMS我的答案中有一个可能的解决方法。 - Columbo
@ComicSansMS:我不知道为什么,但由于某种原因,我一直在考虑检测私有/受保护的构造函数,而问题显然是析构函数的访问说明符。 Columbo的解决方案确实是我应该想到的。 - Andy Prowl