C++ g++;:合并模板成员函数实例
有没有办法将模板类的相同成员函数合并到最终的二进制文件中?我有一个类,它可能支持特定的功能,并依赖于此,可能需要在成员函数内部执行一些额外的步骤:C++ g++;:合并模板成员函数实例,c++,c++11,templates,optimization,g++,C++,C++11,Templates,Optimization,G++,有没有办法将模板类的相同成员函数合并到最终的二进制文件中?我有一个类,它可能支持特定的功能,并依赖于此,可能需要在成员函数内部执行一些额外的步骤: template <uint32_t features> class Driver { static bool set (uint32_t value) { /* do something required for every feature */ if (features & Feature::A)
template <uint32_t features>
class Driver {
static bool set (uint32_t value) {
/* do something required for every feature */
if (features & Feature::A)
/* do special things if Feature::A */
/* do something required for every feature */
if (features & Feature::B || features & Feature::C)
/* do something special for either Feature::B or Feature::C */
return true;
}
/* more, similar methods */
};
此外,如果其中一个特殊部分中存在“提前退出”路径,则这也会带来问题,以前可以通过简单的返回来完成,现在会以某种方式从子函数升级,根据这些子函数的返回值引入进一步的检查
我想要的是,编译器(在使用时)为Driver::set
和Driver::set
发出符号,但让它们指向相同的代码位置,因为它们通过代码是相同的。你知道我该怎么做吗?(最好使用c++11,不需要更新标准的功能)
编辑:为了澄清,我想知道为什么相同的指令序列(在这里由模板实例生成)不是由gcc
组合而成的。就我的理解而言,-ftree-tail merge
至少应该用跳转到相同的实现/代码序列(例如Driver::set
和Driver::set
)来替换这些实现/代码序列。从您发布的代码中,我很清楚您误用了模板。正确的方法是定义驱动程序的部分专门化:
template <uint32_t features>
class Driver; // There may be some default implementation, but it is not necessary
// Specialize for feature A only
template <>
class Driver<Feature::A> {
bool set (uint32_t value) {
/* do special things for Feature::A */
/* do something required for every feature */
return true;
}
};
// Specialize for feature B only
template <>
class Driver<Feature::B> {
bool set (uint32_t value) {
/* do special things for Feature::B */
/* do something required for every feature */
return true;
}
};
// Specialize for features A and B
template <>
class Driver<Feature::A | Feature::B> {
bool set (uint32_t value) {
/* do special things for Feature::A */
/* do special things for Feature::B */
/* do something required for every feature */
return true;
}
};
你真的需要一个类模板吗?因为这不是模板的使用方式:您正在运行时检查编译时常量:if(features&Feature::A)
它实际上不是编译时常量。应该使用的功能依赖于运行时,在编译过程中是未知的。取决于运行时环境,我可以使用Feature::A
,也可以不使用它。Driver::set
调用不是直接发出的,而是由函数指针发出的,该指针在初始化过程中根据检测到的环境功能进行分配。我看不出您的第一个解决方案对我的给定解决方案有什么好处。在不断折叠之后,模板实例化的代码将是相同的——使用回退,公共部分必须由您的解决方案编写多次(在每个主体中)。随着未来的变化,一个人可能会错过一个可能会带来问题/错误的主体。模板用于减少运行时开销,因为我知道在每次调用时哪些功能可用。我在开始时有一个选择,以选择正确的控制流。问题是,为什么gcc
不合并相同的代码部分。你是对的。这就是为什么这里不需要模板,而应该使用第二种解决方案。我只是举了一个例子,说明如果您决定需要模板,应该如何使用它们。在第二个解决方案中,我引入了不需要/不需要的运行时检查。我现在已经在编译时为每个调用一个功能,我有可用的功能,所以这应该是可能的(或也是)。剩下的问题是,如何使gcc
组合产生相同代码的模板实例。例如,Driver::set
和Driver::set
将是相同的,但发出两次-导致大小增加两倍。至于您说我应该使用模板的方式:与我的解决方案相比,您的解决方案有什么好处(因此我将使用您的解决方案)?好处是没有对功能进行运行时检查(即,类似if(features&Feature::A)
)。它们存在于类模板中,并使模板参数毫无用处。如果您非常关心性能,那么您可能只希望编译器优化您的模板实例化并删除不必要的检查,检查的形式为If(true)
或If(false)
。但我认为这是一种非常落后的编程方式。对于C++ 17,你应该有多个手写模板专业;在C++17中,如果constexpr
,您可以使用。如果您确实想坚持使用模板解决方案,您可以尝试在GCC中启用-Os
优化选项。这可能会减少输出二进制大小。
template <uint32_t features>
class Driver; // There may be some default implementation, but it is not necessary
// Specialize for feature A only
template <>
class Driver<Feature::A> {
bool set (uint32_t value) {
/* do special things for Feature::A */
/* do something required for every feature */
return true;
}
};
// Specialize for feature B only
template <>
class Driver<Feature::B> {
bool set (uint32_t value) {
/* do special things for Feature::B */
/* do something required for every feature */
return true;
}
};
// Specialize for features A and B
template <>
class Driver<Feature::A | Feature::B> {
bool set (uint32_t value) {
/* do special things for Feature::A */
/* do special things for Feature::B */
/* do something required for every feature */
return true;
}
};
class Driver {
private:
uint32_t features_;
public:
Driver(uint32_t features) : features_(features) {}
bool set (uint32_t value) {
/* do something required for every feature */
if (features_ & Feature::A)
/* do special things if Feature::A */
/* do something required for every feature */
if (features & Feature::B || features & Feature::C)
/* do something special for either Feature::B or Feature::C */
return true;
}
};