如何检查是否隐式生成移动构造函数? [英] How can i check if a move constructor is being generated implicitly?

查看:209
本文介绍了如何检查是否隐式生成移动构造函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有几个类,我希望检查是否生成默认的移动构造函数。是否有任何方法检查这个(是编译时断言,还是解析生成的目标文件,还是别的什么?)






动机示例:

  class MyStruct:public ComplicatedBaseClass {
std :: vector< std :: string> ; foo; //可能巨大
ComplicatedSubObject bar;
};

如果任何基础的任何成员或复杂...对象的成员类不能移动,将不会生成其隐式移动构造函数,因此可能会 无法优化即使 foo 可移动,也可以进行移动。




我想避免:


  1. A HREF =http://en.cppreference.com/w/cpp/language/move_constructor#Implicitly-declared_move_constructor相对=nofollow>的隐式移动男星代条件,

  2. 明确和递归违约所有受影响的班,他们的基地,他们的成员及MDASH的特殊成员函数;只是为了确保移动的构造可



我已经尝试过以下操作,但不起作用:


  1. 使用 std

  2. 使用 std :: is_move_constructible &MDASH;当有一个拷贝构造函数接受本会成功 const的类型和放大器; ,它在默认情况下产生(的)。

  3. 使用<$ c $
  4. c> nm -C
    来检查move ctor的存在[见下文]。但是,另一种方法是可行的[参见回答]。






如下所示的小型类的生成的符号:

  #include< utility> 

struct MyStruct {
MyStruct(int x):x(x){}
// MyStruct(const MyStruct& rhs):x(rhs.x){}
// MyStruct(MyStruct&& rhs):x(rhs.x){}
int x;
};
int main(){
MyStruct s1(4);
MyStruct s2(s1);
MyStruct s3(std :: move(s1));
return s1.x + s2.x + s3.x; //确保没有优化
}

生成的符号如下所示: / p>

  $ CXXFLAGS = -  std = gnu ++ 11 -O0make -B x; 。/X; echo $ ?; nm -C x | grep MyStruct | cut -d''-f3,4,5 
g ++ -std = gnu ++ 11 -O0 x.cc -ox
12
.pdata $ _ZN8MyStructC1Ei
.pdata $ _ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_
$的.text _ZN8MyStructC1Ei
$的.text _ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_
$ .xdata _ZN8MyStructC1Ei
$ .xdata _ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_
MYSTRUCT :: MYSTRUCT(INT)
标准: :remove_reference< MyStruct&> :: type&&&

当我显式地默认复制和移动ctors(无符号)时,输出是相同的。 p>

使用我自己的复制和移动ctors,输出如下所示:

  $ vim x.cc; CXXFLAGS = -  std = gnu ++ 11 -O0make -B x; 。/X; echo $ ?; nm -C x | grep MyStruct | cut -d''-f3,4,5 
g ++ -std = gnu ++ 11 -O0 x.cc -ox
12
.pdata $ _ZN8MyStructC1Ei
.pdata $ _ZN8MyStructC1EOKS_
$ .pdata _ZN8MyStructC1ERKS_
$ .pdata _ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_
$的.text _ZN8MyStructC1Ei
$的.text _ZN8MyStructC1EOKS_
$的.text _ZN8MyStructC1ERKS_
$的.text $ _ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_ b $ b $ .xdata _ZN8MyStructC1Ei
$ .xdata _ZN8MyStructC1EOKS_
$ .xdata _ZN8MyStructC1ERKS_
$ .xdata _ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_
MYSTRUCT :: MYSTRUCT(INT)
MYSTRUCT :: MYSTRUCT (MyStruct&&)
MyStruct :: MyStruct(MyStruct const&)
std :: remove_reference< MyStruct&> :: type&& std :: move< MyStruct&>(MyStruct&)

将不起作用。






然而,如果目标类有明确的移动构造函数成员,隐式生成的移动构造函数将是可见的为目标类。也就是说使用此代码:

  #include< utility> 

struct Foobar {
Foobar()= default;
Foobar(const Foobar&)= default;
Foobar(Foobar&&){}
};

struct MyStruct {
MyStruct(int x):x(x){}
int x;
Foobar f;
};
int main(){
MyStruct s1(4);
MyStruct s2(s1);
MyStruct s3(std :: move(s1));
return s1.x + s2.x + s3.x; //确保没有优化
}

c $ c> MyStruct 的move ctor,但不是copy ctor,因为它似乎是完全隐式的。我认为编译器生成一个琐碎的内联移动ctor如果可以,一个非平凡的,如果它必须调用其他非平凡的移动ctors。这仍然没有帮助我的追求虽然。


解决方案

声明你要存在特殊的成员函数 MyStruct ,但不要默认您要检查的那些。假设你关心移动函数,并且想要确保移动构造函数 noexcept

  struct MyStruct {
MyStruct()= default;
MyStruct(const MyStruct&)= default;
MyStruct(MyStruct&&)noexcept; // no = default;这里
MyStruct& operator =(const MyStruct&)= default;
MyStruct& operator =(MyStruct&&&;); // or here
};

然后在类定义之外显式地默认它们:

  inline MyStruct :: MyStruct(MyStruct&&)noexcept = default; 
inline MyStruct& MyStruct :: operator =(MyStruct&&)= default;

如果默认函数被隐式定义为已删除,这将触发编译时错误。


I have several classes for which I wish to check whether a default move constructor is being generated. Is there any way to check this (be it a compile-time assertion, or parsing the generated object files, or something else)?


Motivational example:

class MyStruct : public ComplicatedBaseClass {
    std::vector<std::string> foo; // possibly huge
    ComplicatedSubObject bar;
};

If any member of any base or member of either Complicated...Object classes cannot be moved, MyStruct will not have its implicit move constructor generated, and may thus fail to optimize away the work of copying foo, when a move could be done, even though foo is movable.


I wish to avoid:

  1. tediously checking the conditions for implicit move ctor generation,
  2. explicitly and recursively defaulting the special member functions of all affected classes, their bases, and their members—just to make sure a move constructor is available.

I have already tried the following and they do not work:

  1. use std::move explicitly—this will invoke the copy ctor if no move ctor is available.
  2. use std::is_move_constructible—this will succeed when there is a copy constructor accepting const Type&, which is generated by default (as long as the move constructor is not explicitly deleted, at least).
  3. use nm -C to check the presence of move ctor [see below]. However, an alternative approach is viable [see answer].


I tried looking at the generated symbols of a trivial class like this:

#include <utility>

struct MyStruct {
    MyStruct(int x) : x(x) {}
    //MyStruct(const MyStruct& rhs) : x(rhs.x) {}
    //MyStruct(MyStruct&& rhs) : x(rhs.x) {}
    int x;
};
int main() {
    MyStruct s1(4);
    MyStruct s2(s1);
    MyStruct s3(std::move(s1));
    return s1.x+s2.x+s3.x; // make sure nothing is optimized away
}

The generated symbols looks like this:

$ CXXFLAGS="-std=gnu++11 -O0" make -B x; ./x; echo $?; nm -C x | grep MyStruct | cut -d' ' -f3,4,5
g++ -std=gnu++11 -O0    x.cc   -o x
12
.pdata$_ZN8MyStructC1Ei
.pdata$_ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_
.text$_ZN8MyStructC1Ei
.text$_ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_
.xdata$_ZN8MyStructC1Ei
.xdata$_ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_
MyStruct::MyStruct(int)
std::remove_reference<MyStruct&>::type&&

The output is the same when I explicitly default the copy and move ctors (no symbols).

With my own copy and move ctors, the output looks like this:

$ vim x.cc; CXXFLAGS="-std=gnu++11 -O0" make -B x; ./x; echo $?; nm -C x | grep MyStruct | cut -d' ' -f3,4,5
g++ -std=gnu++11 -O0    x.cc   -o x
12
.pdata$_ZN8MyStructC1Ei
.pdata$_ZN8MyStructC1EOKS_
.pdata$_ZN8MyStructC1ERKS_
.pdata$_ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_
.text$_ZN8MyStructC1Ei
.text$_ZN8MyStructC1EOKS_
.text$_ZN8MyStructC1ERKS_
.text$_ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_
.xdata$_ZN8MyStructC1Ei
.xdata$_ZN8MyStructC1EOKS_
.xdata$_ZN8MyStructC1ERKS_
.xdata$_ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_
MyStruct::MyStruct(int)
MyStruct::MyStruct(MyStruct&&)
MyStruct::MyStruct(MyStruct const&)
std::remove_reference<MyStruct&>::type&& std::move<MyStruct&>(MyStruct&)

So it appears this approach also doesn't work.


However if the target class has a member with explicit move constructor, the implicitly generated move constructor will be visible for the target class. I.e. with this code:

#include <utility>

struct Foobar {
    Foobar() = default;
    Foobar(const Foobar&) = default;
    Foobar(Foobar&&) {}
};

struct MyStruct {
    MyStruct(int x) : x(x) {}
    int x;
    Foobar f;
};
int main() {
    MyStruct s1(4);
    MyStruct s2(s1);
    MyStruct s3(std::move(s1));
    return s1.x+s2.x+s3.x; // make sure nothing is optimized away
}

I will get the symbol for MyStruct's move ctor, but not the copy ctor, as it appears to be fully implicit. I presume the compiler generates a trivial inlined move ctor if it can, and a non-trivial one if it must call other non-trivial move ctors. This still doesn't help me with my quest though.

解决方案

Declare the special member functions you want to exist in MyStruct, but don't default the ones you want to check. Suppose you care about the move functions and also want to make sure that the move constructor is noexcept:

struct MyStruct {
    MyStruct() = default;
    MyStruct(const MyStruct&) = default;
    MyStruct(MyStruct&&) noexcept; // no = default; here
    MyStruct& operator=(const MyStruct&) = default;
    MyStruct& operator=(MyStruct&&); // or here
};

Then explicitly default them, outside the class definition:

inline MyStruct::MyStruct(MyStruct&&) noexcept = default;
inline MyStruct& MyStruct::operator=(MyStruct&&) = default;

This triggers a compile-time error if the defaulted function would be implicitly defined as deleted.

这篇关于如何检查是否隐式生成移动构造函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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