为什么我需要在map :: emplace中使用piecewise_construct作为非可复制对象的单个arg构造函数? [英] why do i need to use piecewise_construct in map::emplace for single arg constructors of noncopyable objects?

查看:227
本文介绍了为什么我需要在map :: emplace中使用piecewise_construct作为非可复制对象的单个arg构造函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下代码将不会在gcc 4.8.2上编译。
问题是这个代码将尝试复制构造一个 std :: pair< int,A> ,由于

gcc在这里失败,还是我缺少某些东西?

  #include< map> 
struct A
{
int bla;
A(int blub):bla(blub){}
A(A&&)= delete;
A(const A&)= delete;
A&运营商=(A&&)= delete;
A& operator =(const A&)= delete;
};
int main()
{
std :: map< int,A>地图;
map.emplace(1,2); //不工作
map.emplace(std :: piecewise_construct,
std :: forward_as_tuple(1),
std :: forward_as_tuple(2)
); //像一个charm
返回0;
}


解决方案

据我所知,这个问题不是由 map :: emplace 引起的,而是由 pair 的构造函数引起的:

  #include< map> 

结构A
{
A(int){}
A(A&&)= delete;
A(A const&)= delete;
};

int main()
{
std :: pair< int,A> x(1,4); //错误
}

此代码示例不编译,既不与coliru的g + +4.8.1也不是clang ++ 3.5,它们都使用libstdc ++,只要我能告诉。



这个问题植根于事实,虽然我们可以构建

  A t(4); 

std :: is_constructible< A,int> :: value == true ,我们不能将 int 隐式转换为 A [conv] / 3


表达式 e 可以是>如果且仅当声明 T t = e; 很好的时候,将其隐式转换为 T 一个发明的临时变量
code> t 。


请注意复制初始化( = )。这将创建一个临时的 A 并从这个临时的[dcl.init] / 17初始化 t 。来自临时的初始化尝试调用 A 的删除的移动ctor,这使得转换不正确。






由于我们无法从 int 转换为 A pair 的构造函数被SFINAE拒绝。拒绝。这个行为是令人惊讶的, N4387 - 改善对和元组分析并尝试通过使构造函数显式而不是拒绝它来改善这种情况。 N4387已经在Lenexa会议上被投入到C ++ 1z中。



以下描述了C ++ 11规则。



我预期被调用的构造函数在[pairs.pair] / 7-9


中描述code> template< class U,class V> constexpr对(U&& x,V&& y); 

7   需要: is_constructible< first_type,U&&> :: value true
is_constructible< second_type,V&&> :: value true 。 >

8  
构造函数首先用 std :: forward< U>(x)初始化,第二个用
std :: forward< V>(y)



9  说明:如果 U 不能隐式转换为
first_type V 不能隐式转换为 second_type
构造函数不得参与重载解析。


请注意 部分中的 is_constructible 之间的区别,是 部分。满足要求来调用此构造函数,但它可能不会参与重载解析(=必须通过SFINAE拒绝)。



因此,重载分辨率需要选择一个更糟糕的匹配,即第二个参数是 A const& 的人。一个临时文件是从 int 参数创建的,并且绑定到此引用,引用用于初始化数据成员( .second )。初始化尝试调用 A 的已删除副本,并且该对的构造不正确。






libstdc ++有(作为扩展)一些非标准ctors。在最新的doxygen (和4.8.2 ),我预期被调用的的构造函数(令本标准要求的规则感到惊讶)是:

 模板< class _U1,class _U2,
class = typename enable_if< __ and_< is_convertible< _U1,_T1>,
is_convertible< _U2,_T2>
> :: value
> :: type>
constexpr对(_U1& __x,_U2& __y)
:first(std :: forward< _U1>(__ x)),second(std :: forward< _U2>(__ y) ){}

而实际 标准:

  // DR 811. 
template< class _U1,
class = typename enable_if< is_convertible ; _U1,_T1> :: value> :: type>
constexpr pair(_U1& __x,const _T2& __y)
:first(std :: forward< _U1>(__ x)),second(__ y){}

该程序根据标准格式不正确,仅不适用于此非标准ctor。






作为最后的评论,这里是 is_constructible is_convertible



is_constructible [meta.rel ] / 4


给出以下函数原型:

 code> template< class T> 
typename add_rvalue_reference< T> :: type create();

模板专门化的谓词条件 is_constructible< T,Args ... < / code>将被满足,当且仅当以下变量定义对于某些发明变量 t 是良好的:

  T t(创建< Args>()...); 

[注意:这些标记永远不会被解释为函数声明。 - end note ]执行访问检查,就像在与 T 无关的上下文中,任何 Args 。只考虑变量初始化的直接上下文的有效性。


is_convertible [meta.unary.prop] / 6:


给定以下函数原型:

 模板< class T> 
typename add_rvalue_reference< T> :: type create();

模板专门化的谓词条件 is_convertible< From,To>如果
只有在以下代码中的返回表达式格式正确的情况下,才会满足code>,包括函数返回类型的任何隐式转换

 要测试(){
return create< From>();
}

[注意:这个要求给出了明确的结果用于引用类型,void类型,数组类型和函数类型。 - end note ]执行访问检查,就像在与 To 之间无关的上下文中。只需
就会考虑到 return-statement 的表达式的直接上下文的有效性(包括转换为
的返回类型)。







对于您的类型 A

  A t(create< int>()); 

格式正确;然而

  A test(){
return create< int>();
}

创建一个临时类型 A 并尝试将移动到返回值(复制初始化)中。选择删除的ctor A(A&&),因此形式不正确。


The following code will not compile on gcc 4.8.2. The problem is that this code will attempt to copy construct an std::pair<int, A> which can't happen due to struct A missing copy and move constructors.

Is gcc failing here or am I missing something?

#include <map>
struct A
{
  int bla;
  A(int blub):bla(blub){}
  A(A&&) = delete;
  A(const A&) = delete;
  A& operator=(A&&) = delete;
  A& operator=(const A&) = delete;
};
int main()
{
  std::map<int, A> map;
  map.emplace(1, 2); // doesn't work
  map.emplace(std::piecewise_construct,
          std::forward_as_tuple(1),
          std::forward_as_tuple(2)
  ); // works like a charm
  return 0;
}

解决方案

As far as I can tell, the issue isn't caused by map::emplace, but by pair's constructors:

#include <map>

struct A
{
    A(int) {}
    A(A&&) = delete;
    A(A const&) = delete;
};

int main()
{
    std::pair<int, A> x(1, 4); // error
}

This code example doesn't compile, neither with coliru's g++4.8.1 nor with clang++3.5, which are both using libstdc++, as far as I can tell.

The issue is rooted in the fact that although we can construct

A t(4);

that is, std::is_constructible<A, int>::value == true, we cannot implicitly convert an int to an A [conv]/3

An expression e can be implicitly converted to a type T if and only if the declaration T t=e; is well-formed, for some invented temporary variable t.

Note the copy-initialization (the =). This creates a temporary A and initializes t from this temporary, [dcl.init]/17. This initialization from a temporary tries to call the deleted move ctor of A, which makes the conversion ill-formed.


As we cannot convert from an int to an A, the constructor of pair that one would expect to be called is rejected by SFINAE. This behaviour is surprising, N4387 - Improving pair and tuple analyses and tries to improve the situation, by making the constructor explicit instead of rejecting it. N4387 has been voted into C++1z at the Lenexa meeting.

The following describes the C++11 rules.

The constructor I had expected to be called is described in [pairs.pair]/7-9

template<class U, class V> constexpr pair(U&& x, V&& y);

7    Requires: is_constructible<first_type, U&&>::value is true and is_constructible<second_type, V&&>::value is true.

8    Effects: The constructor initializes first with std::forward<U>(x) and second with std::forward<V>(y).

9    Remarks: If U is not implicitly convertible to first_type or V is not implicitly convertible to second_type this constructor shall not participate in overload resolution.

Note the difference between is_constructible in the Requires section, and "is not implicitly convertible" in the Remarks section. The requirements are fulfilled to call this constructor, but it may not participate in overload resolution (= has to be rejected via SFINAE).

Therefore, overload resolution needs to select a "worse match", namely one whose second parameter is a A const&. A temporary is created from the int argument and bound to this reference, and the reference is used to initialize the pair data member (.second). The initialization tries to call the deleted copy ctor of A, and the construction of the pair is ill-formed.


libstdc++ has (as an extension) some nonstandard ctors. In the latest doxygen (and in 4.8.2), the constructor of pair that I had expected to be called (being surprised by the rules required by the Standard) is:

template<class _U1, class _U2,
         class = typename enable_if<__and_<is_convertible<_U1, _T1>,
                                           is_convertible<_U2, _T2>
                                          >::value
                                   >::type>
constexpr pair(_U1&& __x, _U2&& __y)
: first(std::forward<_U1>(__x)), second(std::forward<_U2>(__y)) { }

and the one that is actually called is the non-standard:

// DR 811.
template<class _U1,
         class = typename enable_if<is_convertible<_U1, _T1>::value>::type>
constexpr pair(_U1&& __x, const _T2& __y)
: first(std::forward<_U1>(__x)), second(__y) { }

The program is ill-formed according to the Standard, it is not merely rejected by this non-standard ctor.


As a final remark, here's the specification of is_constructible and is_convertible.

is_constructible [meta.rel]/4

Given the following function prototype:

template <class T>
typename add_rvalue_reference<T>::type create();

the predicate condition for a template specialization is_constructible<T, Args...> shall be satisfied if and only if the following variable definition would be well-formed for some invented variable t:

T t(create<Args>()...);

[Note: These tokens are never interpreted as a function declaration. — end note] Access checking is performed as if in a context unrelated to T and any of the Args. Only the validity of the immediate context of the variable initialization is considered.

is_convertible [meta.unary.prop]/6:

Given the following function prototype:

template <class T>
typename add_rvalue_reference<T>::type create();

the predicate condition for a template specialization is_convertible<From, To> shall be satisfied if and only if the return expression in the following code would be well-formed, including any implicit conversions to the return type of the function:

To test() {
  return create<From>();
}

[Note: This requirement gives well defined results for reference types, void types, array types, and function types. — end note] Access checking is performed as if in a context unrelated to To and From. Only the validity of the immediate context of the expression of the return-statement (including conversions to the return type) is considered.


For your type A,

A t(create<int>());

is well-formed; however

A test() {
  return create<int>();
}

creates a temporary of type A and tries to move that into the return-value (copy-initialization). That selects the deleted ctor A(A&&) and is therefore ill-formed.

这篇关于为什么我需要在map :: emplace中使用piecewise_construct作为非可复制对象的单个arg构造函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆