当前位置: 首页 > 知识库问答 >
问题:

比较两个map::迭代器:为什么需要std::对的复制构造函数?

全誉
2023-03-14

下面非常简单的代码在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
}

标准=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(trunk 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.

我一直在看bit/stl_tree. h中的代码,我不明白为什么它试图实例化std::对

为什么在C 11中需要复制构造函数std::pair?

注:上述代码是从不可复制映射的映射迭代器上不支持的等式运算符(=)中提取的

解决方案

这里有两个不幸的问题。

质量差的错误消息:第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个答案

宣滨海
2023-03-14

我想我是在试图减少错误后发现的。首先,似乎不需要进行比较就可以使程序格式错误。然后,错误消息包含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的行:

错误:此显式默认复制构造函数的参数为常量,但成员或基要求它为非常量

 constexpr pair(const pair&) = default;

[dcl.fct.def.def.default]的提示/4

用户提供的显式默认函数(即在其第一次声明后显式默认)在显式默认点定义;如果这样的函数被隐式定义为已删除,则程序格式错误。

[强调地雷]

如果像我假设的那样,[class.copy]/11说应该将这个ctor定义为deleted,那么它将被定义为立即删除,而不仅仅是在使用odr时。因此,不需要实例化来使程序格式错误。

牛凌
2023-03-14

std::map使用std::pair存储键值对,其中键(第一个元素)是常量。

编译器错误与std::pair所需的复制构造函数有关,即使它没有被使用(我认为不是)。

<代码>标准::对

隐式构造函数将具有签名T::T(const T

A的复制构造函数没有满足此要求,因此std::pair::pair对STL的签名错误,这需要T::T(const T

李兴为
2023-03-14

在这种情况下,不需要std::pair的copy构造函数,但由于它是在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 pair(const pair&) = default;
 类似资料:
  • 考虑一个大型内存容器。在这个简单的例子中 span允许我在内存上创建一个轻量级视图。现在,我只想打印跨度: 输出: 现在我想制作子集(这是实际上作为视图变得有用的地方)。我可以使用迭代器来指定我的范围并从调用这个构造函数(3) 但这行不通: C没有与参数列表匹配的构造函数实例参数类型为:(std::\u Vector\u iterator 有可能使用接受指针和大小的构造函数(2): 但这违背了迭代

  • 我想知道为什么c标准要求只接受随机访问迭代器?我不认为这有什么好处,因为std::sort和std::list::sort的复杂性都是。将限制为随机访问迭代器(RAI),似乎需要为具有相同复杂性的列表编写单独的函数。 同样的情况也适用于,其中列表的非RAI计数器部分至今仍然缺失。 这种设计是因为历史上人们使用了的变体来实现? 如果在RAI容器上编写排序算法有好处,那么最好使更通用,并让像这样的RA

  • 我想知道为什么std::map和std::set使用std::less作为比较键的默认函子。为什么不使用一个类似strcmp的函子呢?类似于: 假设地图中有两个对象,分别是键1和键2。现在,我们要插入另一个具有键3的对象。 如果使用,那么只需一个调用就有足够的信息来做出正确的决定。 根据映射中键的类型,可能很昂贵。 除非我缺少一些非常基本的东西,否则不应该使用类似于比较的东西,而不是作为比较键的默

  • 问题内容: 必须使用无参数构造函数(像Hibernate这样的工具会在此构造函数上使用反射来实例化对象)。 我得到了这个手挥手的答案,但是有人可以进一步解释吗?谢谢 问题答案: hibernate,并且通常通过反射创建对象的代码用于创建类的新实例。此方法需要一个公共的无参数构造函数才能实例化该对象。对于大多数用例,提供无参数构造函数不是问题。 有一些基于序列化的技巧可以解决没有no-arg构造函数

  • 问题内容: 这对您来说可能听起来很愚蠢,但是为什么我需要在s中定义一个空的构造函数? 我看到的每个教程都说:每个实体都需要一个空的构造函数。 但是Java总是给您一个默认的不可见的空构造函数(如果您不重新定义一个的话)。 谢谢。 我认为这是一个语义问题。我所理解的“需求”是书面的。 含义:始终在您的实体中编写一个空的构造函数。 例: 但是,当您不重新定义Java时,Java总是会为您提供一个空的构

  • 问题内容: 我对此代码有疑问:https : //github.com/reactjs/redux/blob/master/examples/async/containers/App.js 特别: 我猜这是一个两部分的问题。 为什么我需要将句柄更改设置为类的实例,我不能只对handleChange使用静态函数并直接在类中调用它 ? 我不知道这是怎么回事: 谢谢 问题答案: 以相反的顺序回答… 返回