std::any 用于仅移动模板,其中 copy-ctor 内的 static_assert 等于编译错误,但为啥呢?

Posted

技术标签:

【中文标题】std::any 用于仅移动模板,其中 copy-ctor 内的 static_assert 等于编译错误,但为啥呢?【英文标题】:std::any for move-only template where static_assert inside copy-ctor equals compile error, but why?std::any 用于仅移动模板,其中 copy-ctor 内的 static_assert 等于编译错误,但为什么呢? 【发布时间】:2019-11-11 21:16:14 【问题描述】:

我不明白为什么只有移动模板不能通过具有static_assert(如下面的代码)的copy-ctor来扩展,以便与std::any一起使用

#include <any>
#include <cassert>

namespace detail
template<typename T=int>
struct MoveOnly

    MoveOnly() = default;
    MoveOnly(MoveOnly const&) static_assert(sizeof(T)!=sizeof(T),"");
    MoveOnly(MoveOnly &&) = default;
    MoveOnly &operator=(MoveOnly const&) static_assert(sizeof(T)!=sizeof(T),"");
    MoveOnly &operator=(MoveOnly &&) = default;
;

using MoveOnly = detail::MoveOnly<>;
static_assert(std::is_copy_constructible<MoveOnly>::value,"");

int main() 
    MoveOnly a;
    //std::any any(std::move(a)); //<- compile error
    return 0;

在std::any::any 中表示 ctor #4

此重载仅在 ... std::is_copy_constructible_v<:decay_t>> 为真时参与重载决议。

据我所知,std::is_copy_constructible&lt;MoveOnly&gt;::value 给出了 true 并且永远不会调用 copy-ctor。那么编译器怎么可能还在抱怨 static_assert 里面的 copy-ctor 呢?

【问题讨论】:

不是 100% 肯定,但我想std::anystd::function 非常相似,因为它需要通过多态来实现,所以它别无选择,只能要求复制-其分配内容的可构造性。 @Frank 我明白这一点,他们用std::is_copy_constructible&lt;MoveOnly&gt;::value 进行检查,对吗?所以我设法将该值设置为 true,所以我看不出他们最终如何在不直接调用 copy-ctor 的情况下找出static_assert 我不明白这个问题。将选择 ctor #4,因为 std::is_copy_constructible_v&lt;std::decay_t&lt;ValueType&gt;&gt;true。这意味着你制作了一个副本,这意味着你的static_assert 会触发。 @NathanOliver-ReinstateMonica 不,据我所知,#4 仅在 std::is_copy_constructible_v&lt;std::decay_t&lt;ValueType&gt;&gt;true 时启用。但是如果你使用 std::move 它仍然使用 move-ctor 该函数无论如何都会被创建,因为它被辅助类型的 vtable 指向。这是因为std::any 可能会被复制到其他翻译单元中的另一个std::any。复制构造函数不会在您的代码中调用,但它可能会从其他地方调用。没有办法提前知道。 【参考方案1】:

考虑以下情况:

foo.h:

void foo(const std::any& v);

foo.cpp:

void foo(const std::any& v) 
  std::any tmp = v;

main.cpp:

#include "foo.h"

int main() 
    MoveOnly a;
    std::any any(std::move(a));

    foo(any);

    return 0;

main.cpp内部,无法知道foo()是否会复制v,所以我们别无选择,必须复制-构造函数可用以防万一它可能需要被调用。

编辑:解决 cmets 中提到的问题:

好的。那么我是否理解正确:复制ctor被创建并且static_assert被触发,因为一个简单的指针指向复制ctor?

本质上,是的。一旦一个指针指向一个函数,该函数就必须存在,并且使该函数存在将触发它的代码生成,包括评估其中的任何static_assert()

在您的理解中唯一有点偏离的是指针并不是为了它而存在的。它在那里是因为它很有可能被用来调用指向的函数。也许它会在 LTO 期间得到优化,但为时已晚;到那时code-gen就完成了

【讨论】:

以上是关于std::any 用于仅移动模板,其中 copy-ctor 内的 static_assert 等于编译错误,但为啥呢?的主要内容,如果未能解决你的问题,请参考以下文章

std::any - 为啥它缺少这么多运算符?

如何设计符合标准的 std::any 实现的存储?

std::reference_wrapper<std::any> 上的类型特征

std::any 由 std::exception_ptr

在 natvis 中使用 std::type_info 进行强制转换

使用模板将类型发送到函数中