【问题标题】:C++11 class in std::map as Value with private constructorsstd::map 中的 C++11 类作为具有私有构造函数的值
【发布时间】:2016-11-30 23:17:17
【问题描述】:

这是该类的简化版本,它作为值存储在地图中,在 VS2008 中可以正常工作(请注意,所有成员都是私有的):

class Value{
    friend class FriendClass;
    friend class std::map<std::string, Value>;
    friend struct std::pair<const std::string, Value>;
    friend struct std::pair<std::string, Value>;

    Value() {..}
    Value(Value const& other) {..}

    ... rest members...
};

代码(从 FriendClass 调用,因此可以到达私有构造函数):

FriendClass::func()
{
    std::map<const std::string, Value> map;
    map.insert(std::make_pair(std::string("x"), Value()));
}

这在 VS2008 中编译没有任何错误,但在 VS2015/C++11 上失败:

file.cpp(178): 错误 C2664: 'std::_Tree_iterator>>> std::_Tree>::insert(std::_Tree_const_iterator>>>,const std::pair &)': 不能从 ' 转换参数 1 std::pair' 到 'std::pair &&' 和 [ _Kty=std::字符串, _Ty=值, _Pr=std::less, _Alloc=std::分配器> ] 和 [ _Kty=std::字符串, _Ty=值 ] file.cpp(178):注意:原因:无法从 'std::pair' 转换为 'std::pair' 和 [ _Kty=std::字符串, _Ty=值 ] file.cpp(178):注意:没有可以执行此转换的用户定义转换运算符,或者无法调用该运算符

如果我将 Value 复制构造函数公开,它在 VS2015 中也可以正常编译。

但这是有目的的私有化,并且仅适用于 std::map 和 std::pair。但是,在 C++11 中似乎还需要声明额外的好友访问权限。这些是什么?

谢谢。

【问题讨论】:

  • 如果我用std::string("x")而不是普通的char[]调用make_pair,你的代码会在VS2015中为我编译
  • @Zereges:这很奇怪,因为问题不同。即使我更改为std::string("x") 我也有同样的错误。让我更改示例以避免混淆。你确定你所有的构造函数都是私有的吗?

标签: c++11 private stdmap


【解决方案1】:

我无法访问您提到的编译器,但这是我在 g++ 5.3 上看到的。

考虑以下与您的问题基本相同的版本:

#include <map>                                                                                                                             
#include <utility>


class foo
{
    friend std::pair<const int, foo>;

    foo(const foo &other){}

public:
    foo(){}
};


int main()
{
    using map_t = std::map<int, foo>;

    map_t m;
    m.insert(std::make_pair(2, foo()));
    // m.emplace(2, foo());
}

(默认 ctor 是 public,但这不是必需的,只会使示例更短。)

main 中,注意这两行

m.insert(std::make_pair(2, foo()));
// m.emplace(2, foo());

反转 cmets 构建良好,但显示的版本没有:

/usr/include/c++/5/bits/stl_pair.h: In instantiation of ‘constexpr    std::pair<_T1, _T2>::pair(_U1&&, const _T2&) [with _U1 = int; <template-parameter-2-2> = void; _T1 = int; _T2 = foo]’:
/usr/include/c++/5/bits/stl_pair.h:281:72:   required from ‘constexpr std::pair<typename std::__decay_and_strip<_Tp>::__type, typename   std::__decay_and_strip<_T2>::__type> std::make_pair(_T1&&, _T2&&) [with _T1 = int; _T2 = foo; typename std::__decay_and_strip<_T2>::__type = foo; typename std::__decay_and_strip<_Tp>::__type = int]’
stuff.cpp:21:34:   required from here
stuff.cpp:9:2: error: ‘foo::foo(const foo&)’ is private
 foo(const foo &other){}
 ^

查看源代码std_pair.h 表明它确实在尝试调用复制构造函数。不幸的是,你 friended std::pair,但不是 std::make_pair

emplace 版本没有这个问题,但我怀疑这是依赖于实现的。一般来说,如果你想要一个容器来存储一个完全不透明的类,我建议你对它们使用std::shared_ptrs 的容器。这允许您完全指定哪个函数/类可以在您自己的代码中创建/复制对象,并且不对库的代码做任何假设。

【讨论】:

  • Unfortunately, you friended std::pair, but not std::make_pair. - 我会加好友,但它没有帮助(或者我做错了)。我尝试添加 friend std::pair&lt;std::string, Value&gt; std::make_pair(std::string&amp;&amp;, Value&amp;&amp;); 但同样的错误。我看不出有理由避免emplace() 实际上,这可能是一种方式(甚至更快),但对没有 shared_ptr / emplace / public 构造函数的其他解决方案感到好奇,因为它使用“旧”c ++ w/o 编译问题。
  • 我试图在我的回答中解释为什么我认为它不能干净地完成:你可以为一个类、一个函数或任何东西加好友。但是,只要它不是您的代码,您就不能确定它不会调用其他不是朋友的东西。所以你所拥有的是,在某些编译器的某些版本中emplace 恰好可以工作,而你的旧版本没有make_pair 的重载,这是一个问题,等等。IIUC,没有什么禁止emplace 的下一个版本再次破坏您的代码。
  • 所以public copy constructorshared_ptr 似乎是最好的解决方案。
  • 据我所知 - 是的。不过,也许有人会对我完全没有想到的事情感到惊讶:-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-03
  • 1970-01-01
相关资源
最近更新 更多