首页 文章

比较两个map :: iterators:为什么需要std :: pair的拷贝构造函数?

提问于
浏览
17

下面编译和链接的非常简单的代码,而无需用C 98警告,但给出了用C 11模式不可理解的编译错误 .

#include <map>

struct A {
    A(A& ); // <-- const missing
};

int main() {
    std::map<int, A> m;
    return m.begin() == m.end(); // line 9
}

-std=c++11 是误差,gcc版本4.9.0 20140302(实验)(GCC):

ali@X230:~/tmp$ ~/gcc/install/bin/g++ -std=c++11 cctor.cpp 
In file included from /home/ali/gcc/install/include/c++/4.9.0/bits/stl_algobase.h:64:0,
                 from /home/ali/gcc/install/include/c++/4.9.0/bits/stl_tree.h:61,
                 from /home/ali/gcc/install/include/c++/4.9.0/map:60,
                 from cctor.cpp:1:
/home/ali/gcc/install/include/c++/4.9.0/bits/stl_pair.h: In instantiation of ‘struct std::pair’:
cctor.cpp:9:31:   required from here
/home/ali/gcc/install/include/c++/4.9.0/bits/stl_pair.h:127:17: error: ‘constexpr std::pair::pair(const std::pair&) [with _T1 = const int; _T2 = A]’ declared to take const reference, but implicit declaration would take non-const
       constexpr pair(const pair&) = default;
                 ^

与clang版本3.5(主干202594)

ali@X230:~/tmp$ clang++ -Weverything -std=c++11 cctor.cpp 
In file included from cctor.cpp:1:
In file included from /usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../include/c++/4.7/map:60:
In file included from /usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../include/c++/4.7/bits/stl_tree.h:63:
In file included from /usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../include/c++/4.7/bits/stl_algobase.h:65:
/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../include/c++/4.7/bits/stl_pair.h:119:17: error: the parameter for this explicitly-defaulted copy constructor is const, but
      a member or base requires it to be non-const
      constexpr pair(const pair&) = default;
                ^
cctor.cpp:9:22: note: in instantiation of template class 'std::pair' requested here
    return m.begin() == m.end(); // line 9
                     ^
1 error generated.

我一直在寻找在 bits/stl_tree.h 的代码,我不明白为什么它试图实例 std::pair .

Why does it need the copy constructor of std::pair in C++11?


注意:上面的代码从平等运算符(==)不支持的在 Map 上的迭代器不可复制 Map 萃取 .


SOLUTION

这里有两个不幸的问题 .

质量差的错误消息:第8行应该已经给出了编译错误,尽管错误消息只是抱怨第9行 . 在第8行获得错误将非常有帮助,理解真正的问题会容易得多 . 如果gcc / clang trunk中仍然存在此问题,我可能会提交错误报告/功能请求 .

另一个问题是ecatmur写的 . 请考虑以下代码:

struct A {
    A() = default;
    A(A& ); // <-- const missing
};

template<class T>
struct B {
    B() = default;
    B(const B& ) = default;
    T t;
};

int main() {
  B<A> b;  
}

它无法编译 . 即使不被任何所需要的拷贝构造函数,它仍然实例化,因为它是行内默认,在类的主体;这导致编译错误 . 这可以通过将复制构造函数移出类的主体来修复:

template<class T>
struct B {
    B() = default;
    B(const B& );
    T t;
};

template <class T>
B<T>::B(const B& ) = default;

一切都好 . 不幸的是, std::pair 有一个默认定义的内联复制构造函数 .

3 回答

  • 0

    在这种情况下,不需要 std::pair 的复制构造函数,但由于它是 std::pair 声明中的内联默认定义,因此会自动实例化 std::pair 本身的实例化 .

    标准库可以提供复制构造函数的非内联默认定义:

    template<class _T1, class _T2>
      struct pair
      {
    // ...
        constexpr pair(const pair&);
    // ...
      };
    // ...
    template<class _T1, class _T2>
    constexpr pair<_T1, _T2>::pair(const pair&) = default;
    

    然而,这不会与标准(第20.3.2),其中的拷贝构造函数是默认定义内嵌的严格信一致:

    constexpr对(const的一对&)=默认值;

  • 2

    我想我找到它试图减少错误之后 . 首先,似乎不需要进行比较以使程序格式错误 . 然后,错误消息包含dtor,所以我尝试不实例化dtor . 结果:

    #include <map>
    
    struct A {
        A(A& ); // <-- const missing
    };
    
    int main() {
        std::map<int, A>* m = new std::map<int, A>();
        // note: dtor not (necessarily?) instantiated
    }
    

    但是输出消息仍然包含,现在用于调用 m 的ctor的行:

    error:这个显式默认的复制构造函数的参数是const,但是成员或基数要求它是非const constexpr对(const pair&)= default;

    哪个提示[dcl.fct.def.default] / 4

    由用户提供的显式默认的功能(即,它的第一个声明之后明确的缺省值)是在其被明确地默认点限定;如果将这样的函数隐式定义为已删除,则该程序格式错误 .

    [强调我的]

    如果,正如我所假设的那样,[class.copy] / 11表示应该将此ctor定义为已删除,则将其定义为立即删除 - 不仅仅是在需要使程序格式错误时才需要删除 .

  • 3

    std::map 使用 std::pair 来存储键值对,其中键(第一个元素)是 const .

    编译器错误涉及到需要复制构造 std::pair ,即使它不是't being used (which I don'认为这不是) .

    必须生成 std::pair<int, A> . 这是调用map :: begin时首先需要的 . 由于没有为此类型指定显式复制构造函数,因此使用了隐式复制构造函数 .

    只有当T的所有非静态成员(类型S)都具有复制构造函数S :: S(const S&)时,隐式构造函数才会具有签名T :: T(const T&)(对于T的基础必须保持相同的要求)类型复制构造函数) . 否则,使用具有签名T :: T(T&)的复制构造函数 .

    A的拷贝构造函数失败这一要求,所以性病::对::对对STL的,这需要T :: T(const的T&)签名错误 .

相关问题