替代问题标题是: 如何明确让编译器为特定翻译单元中编译器生成的构造函数生成代码?
我们面临的问题是,对于一个代码路径,如果一个对象的copy-ctor调用是,那么 - 彻底测量 - 性能会更好(大约5%)不内联,即如果手动实现此构造函数。 (我们注意到这一点,因为在代码清理期间,删除了此类(17个成员)的多余明确实现的副本ctor。)
编辑请注意,我们已检查生成的汇编代码,并确保正如我针对两个不同代码版本所描述的那样进行内联和代码生成。
我们现在面临的选择只是放弃手动复制代码(它与编译器生成的代码完全一样)或者找到不的任何其他方法来内联复制代码这堂课。
是否有任何方法(对于Microsoft Visual C ++)在特定的翻译单元中显式实例化编译器生成的类函数,或者它们是否总是在每个使用它们的翻译单元中内联? (欢迎使用gcc或其他编译器的评论来更好地了解情况。)
由于前两个答案显示出一些误解:编译器生成的类函数仅由编译器本身生成,如果它们既未声明也未由用户定义。因此,没有任何修饰符可以应用于它们,因为源代码中的这些函数不存在。
struct A {
std::string member;
};
A
有一个默认和复制ctor,一个dtor和一个复制操作符。这些函数都不能通过某些declspec修改,因为它们不存在于代码中。
struct B {
std::string member;
B(B const& rhs);
};
B
现在有一个用户提供的副本ctor,用户必须实现它。编译器不会为它生成代码。
怀疑者的更多背景:-) ......
此代码使用MS Visual C ++编译,但它是为嵌入式(类似)(实时)系统链接的。通过对这个系统进行计时来衡量绩效,因此我认为参加计划的人会得到一些不错的数据。
通过比较两个代码版本来执行测试,其中仅差异是内联与此一类的非内联副本ctor。带内联代码的计时更差约5%。
进一步检查发现我在某一点上错了:编译器将为复杂的复制构造函数生成单独的函数。它将自行决定这一点,它还取决于优化设置。所以在我们的例子中,编译器在我们的特定情况下做错了。从答案到目前为止,似乎我们不能告诉编译器。 : - (
答案 0 :(得分:5)
$ 12.1 / 5-“隐含声明 默认构造函数是内联的 同类的公共成员。“。
所以我们无能为力。 implcit构造函数必须是内联的。在这方面的任何其他行为可能是一个扩展
话虽如此,
您的手动复制构造函数(在代码清理期间删除)可能是正确的。例如,如果您的类中的一个成员(17个中的一个)是指针成员,则手动复制构造函数可能会处理深层复制(因此会影响性能)。
因此,除非您仔细检查手动复制构造函数,否则不要考虑删除它并依赖于(可能有错误的)隐式复制构造函数(在您的上下文中)
答案 1 :(得分:3)
我非常怀疑内联与它有什么关系。如果编译器内联编译器生成的副本ctor,为什么它也不会内联显式定义的副本? (编译器的优化启发式失败以至于使内联代码慢了5%也是不寻常的)
在得出结论之前,
如果是这种情况,您可以使用此信息更新您的问题吗?
C ++中无法指示是否应该内联编译器生成的函数。甚至特定于供应商的扩展(例如__declspec(noinline)
)也不会对您有所帮助,因为您明确将函数的所有责任移交给编译器。因此编译器选择如何处理它,如何实现它以及是否内联它。你不能同时说“请为我实现这个功能”,同时“请让我控制功能的实现方式”。如果要控制该功能,则必须实现它。 ;)
在C ++ 0x中,可能是可能的(取决于这些特定于供应商的扩展如何与声明为= default
的函数交互)。
但同样,我不相信内联是问题所在。最有可能的是,这两个函数只会导致生成不同的汇编代码。
答案 2 :(得分:0)
文档说它只适用于成员函数,但实际上它也适用于自由函数。
答案 3 :(得分:0)
通常最好将其隔离到您知道存在问题的几种核心类型。例子a:
class t_std_string {
std::string d_string;
public:
/* ... */
/* defined explicitly, and out of line -- you know what to do here */
t_std_string();
t_std_string(const std::string& other);
t_std_string(const t_std_string& other);
~t_std_string();
inline std::string& get() { return this->d_string; }
inline const std::string& get() const { return this->d_string; }
/* ... */
};
struct B {
t_std_string member;
/* 16 more */
/* ... */
};
或者您可以免费获得一些。例子b:
/* B.hpp */
struct B {
private:
/* class types */
struct t_data {
std::string member;
/* 16 more ... */
public:
/* declare + implement the ctor B needs */
/* since it is otherwise inaccessible, it will only hurt build times to make default ctor/dtor implicit (or by implementing them in the header, of course), so define these explicitly in the cpp file */
t_data();
~t_data();
/* allow implicit copy ctor and assign -- this could hurt your build times, however. it depends on the complexity/visibility of the implementation of the data and the number of TUs in which this interface is visible. since only one object needs this... it's wasteful in large systems */
};
private:
/* class data */
t_data d_data;
public:
/* you'll often want the next 4 out of line
-- it depends on how this is created/copied/destroyed in the wild
*/
B();
B(const B& other);
~B();
B& operator=(const B&);
};
/* B.cpp */
/* assuming these have been implemented properly for t_data */
B::B() : d_data() {
}
B::B(const B& other) : d_data(other) {
}
B::~B() {
}
B& B::operator=(const B&) {
/* assuming the default behaviour is correct...*/
this->d_data = other.d_data;
return *this;
}
/* continue to B::t_data definitions */
答案 4 :(得分:0)
您可以使用某种嵌套对象。通过这种方式,嵌套对象的复制构造函数可以保留为免维护的默认值,但是您仍然可以使用显式创建的复制构造函数来声明无内联。
class some_object_wrapper {
original_object obj;
__declspec(noinline) some_object_wrapper(const some_object_wrapper& ref)
: obj(ref) {}
// Other function accesses and such here
};
如果你很绝望,你可以在.lib中单独编译有问题的类并链接到它。将其更改为不同的翻译单元不会阻止VC ++内联它。另外,我不得不质疑他们是否真的在做同样的事情。如果它与默认的复制构造函数相同,为什么要实现手动复制构造函数?
答案 5 :(得分:0)
添加我自己的结论并回答确切的问题而不详细说明:
你不能 强制编译器,特别是VC ++,内联或不内联编译器生成的ctor / dtor / etc。 - 但
优化程序将自行选择 - 如果它为编译器生成的函数(ctor)内联代码,或者为此代码生成“实际”函数。 AFAIK没有办法影响优化器在这方面的决定。