std::is_copy_assignable_v 在 std::unordered_map 上始终为 true。

huangapple go评论71阅读模式
英文:

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 &lt;typename A&gt;
struct testme {
    void operator=(const testme&amp;) {
        static_assert(sizeof(A) == 11111);
    }
};

int main()
{
    std::cout &lt;&lt; std::is_copy_assignable_v&lt;testme&lt;int&gt;&gt;;
}

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&lt;testme&lt;int&gt;&amp;&gt;() = declval&lt;const testme&lt;int&gt;&amp;&gt;

is valid (the assignment operator is declared and not deleted). But it doesn't check testme&lt;int&gt;::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&lt;std::unique_ptr&lt;int&gt;, int&gt; 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&amp; other) -&gt; unordered_map&amp;

should be something like this

auto operator=(const unordered_map&amp; other) -&gt; 
        enable_if_t&lt;is_copy_constructible_v&lt;_Value_type&gt;,
                    unordered_map&amp;&gt;

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&lt;int, testme&lt;int&gt;&gt; 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 &lt;memory&gt;

template&lt;typename T&gt;
class foo
{
    T a;
    public:
    foo() = default;
    foo(foo const&amp; other) : a(other.a)
    {
        
    }
};

template&lt;typename T&gt;
class goo
{
    T a;
    public:
    goo() = default;
    goo(goo const&amp; other) = default;
};

static_assert(std::is_copy_constructible_v&lt;foo&lt;std::unique_ptr&lt;int&gt; &gt; &gt;); // tests if foo(foo const&amp;) is declared - it is
static_assert(!std::is_copy_constructible_v&lt;goo&lt;std::unique_ptr&lt;int&gt; &gt; &gt;); // tests if goo(goo const&amp;) declaration does not exist - it doesn&#39;t 

int main()
{
    foo&lt;std::unique_ptr&lt;int&gt; &gt; b; // compiles despite faulty foo(foo const&amp;) as it is never instantiated
    // foo&lt;std::unique_ptr&lt;int&gt; &gt; c(b); // will fail to compile

    return 0;
}

Basically, compiler views std::unordered_map&lt;int, std::unique_ptr&lt;int&gt;&gt; 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.

huangapple
  • 本文由 发表于 2023年7月18日 16:00:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/76710638.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定