英文:
why std::is_copy_assignable_v alway true when apply on std::unordered_map
问题
我正在尝试使用std::is_copy_assignable_v
来检测一个类是否具有Class& operator=(const Class&)
:
#include <iostream>
#include <memory>
#include <unordered_map>
#include <map>
int main() {
// All of them are true, Why???
std::cout << std::is_copy_assignable_v<std::unordered_map<int, int>> << std::endl;
std::cout << std::is_copy_assignable_v<std::unordered_map<int, std::unique_ptr<int>>> << std::endl;
std::cout << std::is_copy_assignable_v<std::unordered_map<std::unique_ptr<int>, std::unique_ptr<int>>> << std::endl;
std::cout << std::is_copy_assignable_v<std::unordered_map<std::unique_ptr<int>, int>> << std::endl;
return 0;
}
std::unique_ptr
将删除它的复制赋值函数,std::unordered_map
也会这样做,不是吗?
但是编译器显示它具有复制赋值函数,但当我尝试:
std::unordered_map<std::unique_ptr<int>, int> a;
std::unordered_map<std::unique_ptr<int>, int> b;
a = b;
它无法编译。
这似乎也不是编译器的问题(在godbolt上尝试它也是如此)。
英文:
I'm trying to use std::is_copy_assignable_v
to detect whether a class has Class& operator=(const Class&)
:
#include <iostream>
#include <memory>
#include <unordered_map>
#include <map>
int main() {
// All of them are true, Why???
std::cout << std::is_copy_assignable_v<std::unordered_map<int, int>> << std::endl;
std::cout << std::is_copy_assignable_v<std::unordered_map<int, std::unique_ptr<int>>> << std::endl;
std::cout << std::is_copy_assignable_v<std::unordered_map<std::unique_ptr<int>, std::unique_ptr<int>>> << std::endl;
std::cout << std::is_copy_assignable_v<std::unordered_map<std::unique_ptr<int>, int>> << std::endl;
return 0;
}
std::unique_ptr
will delete it's copy assignment function and so will std::unordered_map
, isn't it?
And the compile say it has copy assignment, but when I try:
std::unordered_map<std::unique_ptr<int>, int> a;
std::unordered_map<std::unique_ptr<int>, int> b;
a = b;
It can't compile.
This doesn't seem to be a compiler problem either(try it on godbolt).
答案1
得分: 2
以下是翻译好的代码部分:
这是一个简化的演示。
template <typename A>
struct testme {
void operator=(const testme&) {
static_assert(sizeof(A) == 11111);
}
};
int main()
{
std::cout << std::is_copy_assignable_v<testme<int>>;
}
请注意,我已经将HTML实体编码中的特殊字符进行了还原,以便更好地呈现代码。如果需要更多帮助,请随时提出。
英文:
Here is a simplified demonstration.
template <typename A>
struct testme {
void operator=(const testme&) {
static_assert(sizeof(A) == 11111);
}
};
int main()
{
std::cout << std::is_copy_assignable_v<testme<int>>;
}
The problem here is that is_copy_assignable_v
(or any SFINAE-based check really) does not do deep template instantiation. The standardese for this is "immediate context". In our case is_copy_assignable_v
in effect checks that the expression
declval<testme<int>&>() = declval<const testme<int>&>
is valid (the assignment operator is declared and not deleted). But it doesn't check testme<int>::operator=
body because it is not in the immediate context of the parameter being substituted.
Without changing this fundamental property of the language, the only way to make sure that things like std::unordered_map<std::unique_ptr<int>, int> a;
are not classified as copy assignable is to require that every single template explicitly SFINAEs away its assignment operator. In case of std::unordered_map
, the copy assignment operator declaration, instead of looking like this
auto operator=(const unordered_map& other) -> unordered_map&
should be something like this
auto operator=(const unordered_map& other) ->
enable_if_t<is_copy_constructible_v<_Value_type>,
unordered_map&>
Add here the copy constructor, move assignment, and move constructor, and multiply by the number of templates in the library, and it's a whole lot of work for the library implementors. And it still guarantees nothing. The users should do the same with their templates, otherwise unordered_map<int, testme<int>>
would be still classified as copy assignable.
答案2
得分: 1
以下是您要翻译的代码部分:
作为一个示例,让我展示一下C++允许的荒谬之处
#include <memory>;
template<typename T>
class foo
{
T a;
public:
foo() = default;
foo(foo const& other) : a(other.a)
{
}
};
template<typename T>
class goo
{
T a;
public:
goo() = default;
goo(goo const& other) = default;
};
static_assert(std::is_copy_constructible_v<foo<std::unique_ptr<int>>>); // tests if foo(foo const&) is declared - it is
static_assert(!std::is_copy_constructible_v<goo<std::unique_ptr<int>>>); // tests if goo(goo const&) declaration does not exist - it doesn't
int main()
{
foo<std::unique_ptr<int>> b; // compiles despite faulty foo(foo const&) as it is never instantiated
// foo<std::unique_ptr<int>> c(b); // will fail to compile
return 0;
}
希望这对您有所帮助。
英文:
As an example let me show you the nonsense that C++ permits
#include <memory>
template<typename T>
class foo
{
T a;
public:
foo() = default;
foo(foo const& other) : a(other.a)
{
}
};
template<typename T>
class goo
{
T a;
public:
goo() = default;
goo(goo const& other) = default;
};
static_assert(std::is_copy_constructible_v<foo<std::unique_ptr<int> > >); // tests if foo(foo const&) is declared - it is
static_assert(!std::is_copy_constructible_v<goo<std::unique_ptr<int> > >); // tests if goo(goo const&) declaration does not exist - it doesn't
int main()
{
foo<std::unique_ptr<int> > b; // compiles despite faulty foo(foo const&) as it is never instantiated
// foo<std::unique_ptr<int> > c(b); // will fail to compile
return 0;
}
Basically, compiler views std::unordered_map<int, std::unique_ptr<int>>
to be copy assignable but assignment fails to compile because implementation has bugs. Yet as long as it is never instantiated there won't be any compilation error reports.
I think you can file a defect in STL.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论