(C++ Std 学习) —— 重载函数的地址
概述和背景
在日常学习工作中,很多时候都会用到函数,还要理解函数的地址是如何生成和使用的,不过绝大部分而言都是直接使用类对象指针来调用对应的函数即可,这个过程在内存中其实就是找到对应的对象的其实地址,然后加上该函数在该类对象中的函数地址片偏移量即可找到对应的函数的一个过程。那么,C++Std中又是如何定义关于函数地址的重载的呢,都有哪些细节呢?这就是我这篇文章学习的过程的由来和目的了,希望能够通过记录这篇文章加深自己对于函数的地址的使用方式的理解。
参考文本和自行翻译
除了函数调用表达式之外,如果发生重载解析,重载函数的名称可能出现在以下7种上下文中:
- 对象或引用声明中的初始值设定项;
- 在赋值表达式的右侧;
- 作为函数调用参数;
- 作为用户定义的运算符参数;
- 返回语句;
- 显式强制转换或静态强制转换参数;
- 非类型模板参数;
在每种上下文中,重载函数的名称前面可以加上操作符的地址,并且可以用一组冗余的括号括起来。
在所有这些上下文中,从重载集中选择的函数是其类型与目标所期望的指向函数的指针、指向函数的引用或指向成员函数类型的指针匹配的函数:正在初始化的对象或引用、赋值的左侧、函数或运算符参数,函数的返回类型、强制转换的目标类型或模板参数的类型。
函数的参数类型和返回类型必须与目标完全匹配,不考虑隐式转换(例如,在初始化指向返回基指针的函数的指针时,返回指向派生的指针的函数不会被选中)。
如果函数名命名一个函数模板,那么首先,先尝试模板参数推导,如果推导成功,则生成一个模板用来处理模板专门化,并将其添加到重载集合中进行考虑。不满足关联约束的所有函数都将从集合中删除。(C++ 20)如果集合中的一个以上的函数与目标匹配,并且至少一个函数是非模板的,那么就不用考虑模板专用性。对于任何一个非模板函数,其中一个比另一个约束更大,因此从集合中删除较少的约束函数(因为C++ 20)。如果所有剩余的候选者都是模板专业化的,那么如果有更专业化的候选者,则删除不太专业化的候选者。如果在删除后还有一个以上的候选人留下来,那么这个程序就是格式错误的。
案例代码分析
int f(int) { return 1; }
int f(double) { return 2; }
// g 的参数是两个函数地址的应用,传进来可以调用该函数。
// The parameter of g is the application of two function addresses, and the function can be called when passed in.
void g( int(&f1)(int), int(*f2)(double) ) {}
// 作为模板参数传入函数地址
// Pass in the function address as a template parameter
template< int(*F)(int) >
struct Templ {};
struct Foo {
int mf(int) { return 3; }
int mf(double) { return 4; }
};
struct Emp {
void operator<<(int (*)(double)) {}
};
int main()
{
// 1. initialization
int (*pf)(double) = f; // selects int f(double)
int (&rf)(int) = f; // selects int f(int)
int (Foo::*mpf)(int) = &Foo::mf; // selects int mf(int)
// 2. assignment
pf = nullptr;
pf = &f; // selects int f(double)
// 3. function argument
g(f, f); // selects int f(int) for the 1st argument
// and int f(double) for the second
// 4. user-defined operator
Emp{} << f; //selects int f(double)
// 5. return value
auto foo = []() -> int (*)(int) {
return f; // selects int f(int)
};
// 6. cast
auto p = static_cast<int(*)(int)>(f); // selects int f(int)
// 7. template argument
Templ<f> t; // selects int f(int)
}
OSG的代码中使用分析
namespace osg
{
// 定义模板类,这个类会使用到传进来的模板函数指针
template<typename T>
class TemplateAttributeDispatch : public AttributeDispatch
{
public:
// 定义函数指针的类型,是使用的模板数据作为参数
typedef void (GL_APIENTRY * F) (const T*);
TemplateAttributeDispatch(F functionPtr, unsigned int stride):
_functionPtr(functionPtr), _stride(stride), _array(0) {}
virtual void assign(const GLvoid* array)
{
_array = reinterpret_cast<const T*>(array);
}
virtual void operator () (unsigned int pos)
{
// 使用函数指针来访问对应的函数地址
_functionPtr(&(_array[pos*_stride]));
}
// 定义函数指针对象,用来调用对应的函数的地址内容。
F _functionPtr;
unsigned int _stride;
const T* _array;
};
// 这个类的使用方式与上面的类似
template<typename I, typename T>
class TemplateTargetAttributeDispatch : public AttributeDispatch
{
public:
typedef void (GL_APIENTRY * F) (I, const T*);
TemplateTargetAttributeDispatch(I target, F functionPtr, unsigned int stride):
_functionPtr(functionPtr), _target(target), _stride(stride), _array(0) {}
virtual void assign(const GLvoid* array)
{
_array = reinterpret_cast<const T*>(array);
}
virtual void operator () (unsigned int pos)
{
_functionPtr(_target, &(_array[pos * _stride]));
}
F _functionPtr;
I _target;
unsigned int _stride;
const T* _array;
};
class AttributeDispatchMap
{
public:
AttributeDispatchMap() {}
template<typename T>
void assign(Array::Type type, void (GL_APIENTRY *functionPtr) (const T*), unsigned int stride)
{
if ((unsigned int)type >= _attributeDispatchList.size()) _attributeDispatchList.resize(type+1);
_attributeDispatchList[type] = functionPtr ? new TemplateAttributeDispatch<T>(functionPtr, stride) : 0;
}
// 传入函数指针来作为访问函数地址的使用手段
template<typename I, typename T>
void targetAssign(I target, Array::Type type, void (GL_APIENTRY *functionPtr) (I, const T*), unsigned int stride)
{
if ((unsigned int)type >= _attributeDispatchList.size()) _attributeDispatchList.resize(type+1);
_attributeDispatchList[type] = functionPtr ? new TemplateTargetAttributeDispatch<I,T>(target, functionPtr, stride) : 0;
}
AttributeDispatch* dispatcher(const Array* array)
{
// OSG_NOTICE<<"dispatcher("<<array<<")"<<std::endl;
if (!array) return 0;
Array::Type type = array->getType();
AttributeDispatch* dispatcher = 0;
// OSG_NOTICE<<" array->getType()="<<type<<std::endl;
// OSG_NOTICE<<" _attributeDispatchList.size()="<<_attributeDispatchList.size()<<std::endl;
if ((unsigned int)type<_attributeDispatchList.size())
{
dispatcher = _attributeDispatchList[array->getType()].get();
}
if (dispatcher)
{
// OSG_NOTICE<<" returning dispatcher="<<dispatcher<<std::endl;
dispatcher->assign(array->getDataPointer());
return dispatcher;
}
else
{
// OSG_NOTICE<<" no dispatcher found"<<std::endl;
return 0;
}
}
typedef std::vector< ref_ptr<AttributeDispatch> > AttributeDispatchList;
AttributeDispatchList _attributeDispatchList;
};
//
// With inidices
//
// 下面这些就是利用上面的类传入对应的函数地址的一种方式的过程
AttributeDispatch* AttributeDispatchers::vertexAttribDispatcher(unsigned int unit, Array* array)
{
if (unit>=_vertexAttribDispatchers.size()) assignVertexAttribDispatchers(unit);
return _vertexAttribDispatchers[unit]->dispatcher(array);
}
void AttributeDispatchers::assignVertexAttribDispatchers(unsigned int unit)
{
GLExtensions* extensions = _state->get<GLExtensions>();
for(unsigned int i=_vertexAttribDispatchers.size(); i<=unit; ++i)
{
_vertexAttribDispatchers.push_back(new AttributeDispatchMap());
AttributeDispatchMap& vertexAttribDispatcher = *_vertexAttribDispatchers[i];
vertexAttribDispatcher.targetAssign<GLuint, GLfloat>(i, Array::FloatArrayType, extensions->glVertexAttrib1fv, 1);
vertexAttribDispatcher.targetAssign<GLuint, GLfloat>(i, Array::Vec2ArrayType, extensions->glVertexAttrib2fv, 2);
vertexAttribDispatcher.targetAssign<GLuint, GLfloat>(i, Array::Vec3ArrayType, extensions->glVertexAttrib3fv, 3);
vertexAttribDispatcher.targetAssign<GLuint, GLfloat>(i, Array::Vec4ArrayType, extensions->glVertexAttrib4fv, 4);
}
}
个人格言
用心去感受你自己需要坚持的生活,未来慢慢会给你答案的。