std :: tuple用于不可复制和不可移动的对象 [英] std::tuple for non-copyable and non-movable object
问题描述
我有一堂课,上面有&将ctor删除.
I have a class with copy & move ctor deleted.
struct A
{
A(int a):data(a){}
~A(){ std::cout << "~A()" << this << " : " << data << std::endl; }
A(A const &obj) = delete;
A(A &&obj) = delete;
friend std::ostream & operator << ( std::ostream & out , A const & obj);
int data;
};
我想用该类的对象创建一个元组.但是以下内容无法编译:
And I want to create a tuple with objects of this class. But the following does not compile:
auto p = std::tuple<A,A>(A{10},A{20});
另一方面,以下可以进行编译,但是给出令人惊讶的输出.
On the other hand, the following does compile, but gives a surprising output.
int main() {
auto q = std::tuple<A&&,A&&>(A{100},A{200});
std::cout << "q created\n";
}
输出
~A()0x22fe10 : 100
~A()0x22fe30 : 200
q created
这意味着在元组构造线结束时会立即调用对象的dtor.那么,被破坏的对象元组的重要意义是什么?
It means that dtor for objects is called as soon as tuple construction line ends. So, what is significance of a tuple of destroyed objects?
推荐答案
这很糟糕:
auto q = std::tuple<A&&,A&&>(A{100},A{200});
您正在构造一个rvalue引用的 tuple
元,这些rvalue引用是在表达式末尾被销毁的临时对象,因此剩下的是悬挂的引用.
you are constructing a tuple
of rvalue references to temporaries that get destroyed at the end of the expression, so you're left with dangling references.
正确的陈述是:
std::tuple<A, A> q(100, 200);
但是,直到最近,该标准还不支持上述内容.在N4296中,有关 tuple
的相关构造函数的措辞为[tuple.cnstr]:
However, until quite recently, the above was not supported by the standard. In N4296, the wording around the relevant constructor for tuple
is [tuple.cnstr]:
template <class... UTypes>
constexpr explicit tuple(UTypes&&... u);
要求: sizeof ...(Types)== sizeof ...(UTypes)
. is_constructible< Ti,Ui&> :: value
为true对于所有 i
.
效果:使用 std :: forward< UTypes>(u)
中的相应值初始化元组中的元素.
备注:此构造函数不得参与重载解析,除非 UTypes
中的每种类型为可以隐式转换为类型
中的相应类型.
Requires: sizeof...(Types) == sizeof...(UTypes)
. is_constructible<Ti, Ui&&>::value
is true
for all i
.
Effects: Initializes the elements in the tuple with the corresponding value in std::forward<UTypes>(u)
.
Remark: This constructor shall not participate in overload resolution unless each type in UTypes
is
implicitly convertible to its corresponding type in Types
.
因此,此构造函数未参与重载解析,因为 int
不能隐式转换为 A
.通过采用改进 pair
,这一问题已得到解决.>和 tuple
,它们恰好解决了您的用例:
So, this constructor was not participating in overload resolution because int
is not implicitly convertible to A
. This has been resolved by the adoption of Improving pair
and tuple
, which addressed precisely your use-case:
struct D { D(int); D(const D&) = delete; };
std::tuple<D> td(12); // Error
此构造函数的新措辞来自N4527:
The new wording for this constructor is, from N4527:
备注:除非
sizeof ...(Types)> == 1
和is_constructible< Ti,Ui&> :: value
对于所有i
都是正确的.构造函数是显式的,当且仅当如果至少一个 i 的is_convertible< Ui&&,Ti> :: value
为false
.
Remarks: This constructor shall not participate in overload resolution unless
sizeof...(Types) >= 1
andis_constructible<Ti, Ui&&>::value
is true for alli
. The constructor is explicit if and only ifis_convertible<Ui&&, Ti>::value
isfalse
for at least one i.
并且 is_constructible< A,int&&> :: value
为true.
为了以另一种方式展示差异,这是一个极其简化的元组实现:
To present the difference another way, here is an extremely stripped down tuple implementation:
struct D { D(int ) {} D(const D& ) = delete; };
template <typename T>
struct Tuple {
Tuple(const T& t)
: T(t)
{ }
template <typename U,
#ifdef USE_OLD_RULES
typename = std::enable_if_t<std::is_convertible<U, T>::value>
#else
typename = std::enable_if_t<std::is_constructible<T, U&&>::value>
#endif
>
Tuple(U&& u)
: t(std::forward<U>(u))
{ }
T t;
};
int main()
{
Tuple<D> t(12);
}
如果定义了 USE_OLD_RULES
,则第一个构造函数是唯一可行的构造函数,因此,由于 D
是不可复制的,因此无法编译该代码.否则,第二个构造函数是最佳可行的候选函数,并且结构良好.
If USE_OLD_RULES
is defined, the first constructor is the only viable constructor and hence the code will not compile since D
is noncopyable. Otherwise, the second constructor is the best viable candidate and that one is well-formed.
这种采用是最近的,以至于gcc 5.2和clang 3.6都不会实际编译该示例.因此,您将需要比该版本更新的编译器(gcc 6.0可以运行)或提出不同的设计.
The adoption was recent enough that neither gcc 5.2 nor clang 3.6 actually will compile this example yet. So you will either need a newer compiler than that (gcc 6.0 works) or come up with a different design.
这篇关于std :: tuple用于不可复制和不可移动的对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!