以前一直认为,C++函数的命名取决于函数名以及参数列表。例如,
int foo(int a, float b)g++ 做mangling之后foo的名字为 _Z3fooif
其中: _Z -- reserved
3foo -- the function name "foo"
i -- 1st parameter, int type
f -- 2nd parameter, float type
如果我们同时定义
int foo(int a, float b) {
return 0;
}
float foo(int a, float b) {
return 0;
}
则程序会因重复定义foo(int, float)而无法通过编译:
#g++ simple.cc -o simple
simple.cc: In function ‘float foo(int, float)’:
simple.cc:6:25: error: new declaration ‘float foo(int, float)’
float foo(int a, float b) {
^
simple.cc:2:5: error: ambiguates old declaration ‘int foo(int, float)’
int foo(int a, float b) {
今天在写一个factory的时候,意识到上述修饰规则并不适用于模板函数。模板函数可以具有相同的函数名及参数列表。
这里贴一个非常精简的factory实现(代码部分可跳过)。
Register方法接受BaseType类型的注册,构建string到BaseType到的映射。
Create方法在构造BaseType实例时,根据一个用户给出的string来创建对应类型的实例:
#include <iostream>
#include <string>
#include <memory>
#include <map>
class BaseType {
public:
virtual ~BaseType() {}
virtual std::string& Name() = 0;
};
template <typename T>
class Type1 : public BaseType {
public:
virtual ~Type1() {}
Type1(std::string &name) : name_(name) {}
Type1(BaseType &base_type) : name_(base_type.Name()) {}
virtual std::string& Name() {
return name_;
}
private:
T somedata_;
std::string name_;
};
class Factory {
public:
template <typename T>
void Register(std::string name) {
std::shared_ptr<BaseType> newtype(new T(name));
prototypes_[name] = newtype;
}
template <typename T>
std::shared_ptr<T> Create(std::string name) {
std::shared_ptr<BaseType> instance(new T(*prototypes_[name]));
return std::dynamic_pointer_cast<T>(instance);
}
private:
std::map<std::string, std::shared_ptr<BaseType>> prototypes_;
};
int main() {
Factory factory;
factory.Register<Type1<int>>("Type1_int");
factory.Register<Type1<float>>("Type1_float");
std::shared_ptr<Type1<int>> type_int
= factory.Create<Type1<int>>("Type1_int");
std::shared_ptr<Type1<float>> type_float
= factory.Create<Type1<float>>("Type1_float");
std::cout<<type_int->Name()<<std::endl;
std::cout<<type_float->Name()<<std::endl;
return 0;
}
52和54行的调用会实例化两个Create方法,分别是
std::shared_ptr<Type1<int>> Factory::Create<Type1<int>>(std::string) 和
std::shared_ptr<Type1<float>> Factory::Create<Type1<float>>(std::string)
这两个方法的函数名称"Create"以及参数列表"std::string"完全相同,但是编译器却可以同时创建这两个函数。
这说明这两个方法在做mangling之后其实名字是不一样的。
我们用nm看一下修饰后的名称:
std::shared_ptr<Type1<int>> Factory::Create<Type1<int>>(std::string)
=> _ZN7Factory6Create I5Type1IiEEE St10shared_ptrIT_ESs
std::shared_ptr<Type1<float>> Factory::Create<Type1<float>>(std::string)
=> _ZN7Factory6Create I5Type1IfEEE St10shared_ptrIT_ESs
我们发现模板参数(标红部分)和返回值(标蓝部分)参与了名称的修饰。
这里 I -- template parameter list begin
E -- parameter list end
I5Type1IiEE -- Type1<int>
I5Type1IfEE -- Type1<float>
St -- std::
St10shared_ptrIT_E -- std::shared_ptr<T>
从这个例子看,C++的函数模板与普通函数重载相比,有更强的描述能力。因此通过模板实例化的方式来实现两个版本的foo(int, float)是可行的:
template <typename T>
T foo(int a, float b) {
return 0;
}
int main() {
foo<int>(1, 0);
foo<float>(1, 0);
};
参考文献
IA64 mangling: http://mentorembedded.github.io/cxx-abi/abi.html#mangling