使用虚拟复制构造函数仅移动类型适配 std::any 是安全的吗?
Move only type adapting std::any with dummy copy constructor is safe?
我想用一个只能移动的类型变量来初始化 std::any
。我找到了 。
编译错误案例
在使用链接答案中的 shared_ptr 解决方法之前,我测试了以下代码:
#include <utility>
#include <iostream>
#include <any>
struct move_only {
move_only() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
move_only(move_only const&) = delete;
move_only(move_only &&) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
int main() {
move_only m;
std::any a(std::move(m)); // error. copy constructor is required
}
https://wandbox.org/permlink/h6HOSdgOnQYg4a6K
上面的代码输出编译错误,因为move_only
没有复制构造函数。
为测试添加复制构造函数
我为测试添加了复制构造函数。
#include <utility>
#include <iostream>
#include <any>
struct move_only {
move_only() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
move_only(move_only const&) {
// not called
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
move_only(move_only &&) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
int main() {
move_only m;
std::any a(std::move(m)); // success but copy constructor is not called
}
https://wandbox.org/permlink/kxEnIslmVnJNRSn6
然后编译成功,如我所料。我得到了有趣的输出。
move_only::move_only()
move_only::move_only(move_only &&)
好像没有调用复制构造函数。这让我很惊讶。
我想出了以下包装方法。
添加虚拟复制构造函数包装器
#include <utility>
#include <iostream>
#include <any>
struct move_only {
move_only() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
move_only(move_only const&) = delete;
move_only(move_only &&) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
struct wrapped_move_only : move_only {
wrapped_move_only(move_only&& m):move_only(std::move(m)) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
wrapped_move_only(wrapped_move_only const&) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
assert(false);
}
wrapped_move_only(wrapped_move_only &&) = default;
};
int main() {
move_only m;
wrapped_move_only wmo(std::move(m));
std::any a(std::move(wmo));
}
https://wandbox.org/permlink/EDhq3KPWKP9fCA9v
move_only的复制构造函数被删除。 class wapped_move_only 继承了 move_only 并添加了复制构造函数。
编译成功,得到如下结果
move_only::move_only()
move_only::move_only(move_only &&)
wrapped_move_only::wrapped_move_only(move_only &&)
move_only::move_only(move_only &&)
我似乎使用提供虚拟复制构造函数的包装器用仅移动类型初始化了 std::any。如果目标只是使用仅移动类型初始化 std::any,则使用 shared_ptr 会更有效。这是我的预期行为。
只要我只对std::any
进行移动操作,一旦move_only
移动到std::any
,这段代码安全吗?
如果 std::any
被复制,则由于调用了 wrapped_move_only 的复制构造函数,资产失败。我想知道 move only case 的安全性。
我也不确定为什么 std::any
的目标需要复制构造函数但它没有被调用。
模板化
如果安全的话,我可以使用模板改进这种方法。模板 add_dummy_copy_constructor
是一种适配器。
#include <utility>
#include <iostream>
#include <any>
struct move_only {
move_only() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
move_only(move_only const&) = delete;
move_only(move_only &&) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
template <typename T>
struct add_dummy_copy_constructor : T {
add_dummy_copy_constructor(T&& t):T(std::move(t)) {}
add_dummy_copy_constructor(add_dummy_copy_constructor const&) {
assert(false);
}
add_dummy_copy_constructor(add_dummy_copy_constructor &&) = default;
};
int main() {
move_only m;
std::any a(add_dummy_copy_constructor(std::move(m)));
}
I am also not sure why std::any
's target requires copy constructor but it is not called.
std::any
的设计是成为一种具体类型,可以容纳任何 可复制类型。当你复制 std::any
时,你复制了它下面的任何内容
std::any
需要知道如何 复制底层对象,而不管它是否真的要被复制(它怎么知道这发生?)。因此,如果基础类型不可复制构造,则它一定是一个编译错误。
然而,当我们构建 std::any
本身时,我们知道我们正在构建的具体对象。如果那个具体对象恰好是一个右值,那么我们就可以从构造函数参数移动构造 std::any
的底层对象,而不是复制构造。这是一场免费的胜利。
None 您的代码实际上复制了一个 std::any
,因此它的 none 将调用 std::any
的复制构造函数,后者将调用基础类型的复制构造函数。
类似的事情发生在 std::function
上,也许这里的区别会更明显。当我构造一个 std::function<void()>
时,有一个静态要求,即该对象可以在没有参数的情况下调用。如果它不是 invokabe,则为编译错误。
但是简单地构建 std::function<void()>
不会实际调用底层函数 - 这些是单独的操作。您不会期望此断言会触发:
std::function<void()> f = []{ assert(false); }
我想用一个只能移动的类型变量来初始化 std::any
。我找到了
编译错误案例
在使用链接答案中的 shared_ptr 解决方法之前,我测试了以下代码:
#include <utility>
#include <iostream>
#include <any>
struct move_only {
move_only() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
move_only(move_only const&) = delete;
move_only(move_only &&) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
int main() {
move_only m;
std::any a(std::move(m)); // error. copy constructor is required
}
https://wandbox.org/permlink/h6HOSdgOnQYg4a6K
上面的代码输出编译错误,因为move_only
没有复制构造函数。
为测试添加复制构造函数
我为测试添加了复制构造函数。
#include <utility>
#include <iostream>
#include <any>
struct move_only {
move_only() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
move_only(move_only const&) {
// not called
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
move_only(move_only &&) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
int main() {
move_only m;
std::any a(std::move(m)); // success but copy constructor is not called
}
https://wandbox.org/permlink/kxEnIslmVnJNRSn6
然后编译成功,如我所料。我得到了有趣的输出。
move_only::move_only()
move_only::move_only(move_only &&)
好像没有调用复制构造函数。这让我很惊讶。
我想出了以下包装方法。
添加虚拟复制构造函数包装器
#include <utility>
#include <iostream>
#include <any>
struct move_only {
move_only() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
move_only(move_only const&) = delete;
move_only(move_only &&) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
struct wrapped_move_only : move_only {
wrapped_move_only(move_only&& m):move_only(std::move(m)) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
wrapped_move_only(wrapped_move_only const&) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
assert(false);
}
wrapped_move_only(wrapped_move_only &&) = default;
};
int main() {
move_only m;
wrapped_move_only wmo(std::move(m));
std::any a(std::move(wmo));
}
https://wandbox.org/permlink/EDhq3KPWKP9fCA9v
move_only的复制构造函数被删除。 class wapped_move_only 继承了 move_only 并添加了复制构造函数。
编译成功,得到如下结果
move_only::move_only()
move_only::move_only(move_only &&)
wrapped_move_only::wrapped_move_only(move_only &&)
move_only::move_only(move_only &&)
我似乎使用提供虚拟复制构造函数的包装器用仅移动类型初始化了 std::any。如果目标只是使用仅移动类型初始化 std::any,则使用 shared_ptr 会更有效。这是我的预期行为。
只要我只对std::any
进行移动操作,一旦move_only
移动到std::any
,这段代码安全吗?
如果 std::any
被复制,则由于调用了 wrapped_move_only 的复制构造函数,资产失败。我想知道 move only case 的安全性。
我也不确定为什么 std::any
的目标需要复制构造函数但它没有被调用。
模板化
如果安全的话,我可以使用模板改进这种方法。模板 add_dummy_copy_constructor
是一种适配器。
#include <utility>
#include <iostream>
#include <any>
struct move_only {
move_only() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
move_only(move_only const&) = delete;
move_only(move_only &&) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
template <typename T>
struct add_dummy_copy_constructor : T {
add_dummy_copy_constructor(T&& t):T(std::move(t)) {}
add_dummy_copy_constructor(add_dummy_copy_constructor const&) {
assert(false);
}
add_dummy_copy_constructor(add_dummy_copy_constructor &&) = default;
};
int main() {
move_only m;
std::any a(add_dummy_copy_constructor(std::move(m)));
}
I am also not sure why
std::any
's target requires copy constructor but it is not called.
std::any
的设计是成为一种具体类型,可以容纳任何 可复制类型。当你复制 std::any
时,你复制了它下面的任何内容
std::any
需要知道如何 复制底层对象,而不管它是否真的要被复制(它怎么知道这发生?)。因此,如果基础类型不可复制构造,则它一定是一个编译错误。
然而,当我们构建 std::any
本身时,我们知道我们正在构建的具体对象。如果那个具体对象恰好是一个右值,那么我们就可以从构造函数参数移动构造 std::any
的底层对象,而不是复制构造。这是一场免费的胜利。
None 您的代码实际上复制了一个 std::any
,因此它的 none 将调用 std::any
的复制构造函数,后者将调用基础类型的复制构造函数。
类似的事情发生在 std::function
上,也许这里的区别会更明显。当我构造一个 std::function<void()>
时,有一个静态要求,即该对象可以在没有参数的情况下调用。如果它不是 invokabe,则为编译错误。
但是简单地构建 std::function<void()>
不会实际调用底层函数 - 这些是单独的操作。您不会期望此断言会触发:
std::function<void()> f = []{ assert(false); }