从tr1中function使用看converting constructor
一、converting constructor
不知道为什么,这个名次从来没有听说过,之前也没有关注过C++的这个特性。看了下《The C++ Programming Language》这本书最后的索引,也没有关于这个名词的索引,只是在"constructor"的"and type conversation"条目下有关于这个概念的解释。由于书里的内容不太好拷贝,所以还是从网络上找下这个描述:
Constructors 1 and 2 are both converting constructors in C++03 and C++11. Constructor 3, which must take two arguments, is only a converting constructor in C++11. The last, constructor 4, is not a converting constructor because it is explicit
.
-
C++03: §12.3.1
A constructor declared without the function-specifier
explicit
that can be called with a single parameter specifies a conversion from the type of its first parameter to the type of its class. Such a constructor is called a converting constructor. -
C++11: §12.3.1
A constructor declared without the function-specifier
explicit
specifies a conversion from the types of its parameters to the type of its class. Such a constructor is called a converting constructor.
这个语法本身起始没有什么经验的地方,而且从这个语法定义来看,这个语法适用的范围很小,几乎没有什么应用场景。不过后来回想起在看gcc配套的stl库的tr1::function中也使用了这个语法,所以回过头来看下这个语法在function中的应用。
二、最为直观的语法结构示例
tsecer@harry: cat copy.ctor.cpp
#include <stdio.h>
struct ctor
{
ctor(int i)
{
printf("in int ctor\n");
}
ctor(double d)
{
printf("in double ctor\n");
}
ctor(int i, const char * sz)
{
printf("in int const char \n");
}
};
int main()
{
ctor c = 1, cc = 0.1, cis = {1, ""};
}
tsecer@harry: g++ --version
g++ (GCC) 4.4.6 20110731 (Red Hat 4.4.6-4)
Copyright ? 2010 Free Software Foundation, Inc.
本程序是自由软件;请参看源代码的版权声明。本软件没有任何担保;
包括没有适销性和某一专用目的下的适用性担保。
tsecer@harry: g++ copy.ctor.cpp
copy.ctor.cpp: In function ‘int main()’:
copy.ctor.cpp:21: 错误:在 C++98 中‘cis’必须由构造函数而不是‘{...}’初始化
copy.ctor.cpp:21: 警告:extended initializer lists 只在 -std=c++0x 或 -std=gnu++0x 下可用
tsecer@harry:
三、tr1::function对于该功能的使用
这里看的是早期gcc版本使用的标准库实现:
gcc-4.1.0\libstdc++-v3\include\tr1\functional_iterate.h
template<typename _Res _GLIBCXX_COMMA _GLIBCXX_TEMPLATE_PARAMS>
template<typename _Functor>
function<_Res(_GLIBCXX_TEMPLATE_ARGS)>
::function(_Functor __f,
typename __enable_if<_Useless,
!is_integral<_Functor>::value>::__type)
: _Function_base()
{
typedef _Function_handler<_Signature_type, _Functor> _My_handler;
if (_My_handler::_M_not_empty_function(__f)) {
_M_invoker = &_My_handler::_M_invoke;
_M_manager = &_My_handler::_M_manager;
_My_handler::_M_init_functor(_M_functor, __f);
}
}
下面看到对于特定的实例构造都是直接通过赋值初始化的,而这个赋值初始化本身的底层语法支持其实就是这个看似非常不起眼的converting constructor来完成的。注意:在对数组TF tf[3]初始化过程中,是通过“赋值”来完成对类构造函数的执行,而不是直接使用大家耳熟能详的 TF(f) 来完成。
tsecer@harry: cat function.converting.constructor.cpp
#include <tr1/functional>
#include <stdio.h>
typedef std::tr1::function<void (int, int)> TF;
void f(int, int)
{
printf("in function\n");
}
struct S
{
void operator() (int, int)
{
printf("int struct\n");
}
};
void vv()
{
printf("int void void\n");
}
int main()
{
TF tf[3] =
{
f,
S(),
std::tr1::bind(vv),
};
for (int i = 0; i < 3; i++)
{
tf[i](i, i);
}
}
tsecer@harry: g++ function.converting.constructor.cpp
tsecer@harry: ./a.out
in function
int struct
int void void
四、再回首function和bind之间的关系
从上面的代码可以看到,其实bind和function之间没有必然关系,它只是适配(或者说凭空生成)function中指定的接口类型,然后将这些调用转发给bind的接口及参数。从下面可以看到bind返回值和function本身没有任何关系,它返回的只是一个特殊的_Bind类型。简言之,离了function,bind照样可以用的很溜。
tsecer@harry: cat pure.call.cpp
#include <tr1/functional>
#include <stdio.h>
typedef std::tr1::function<void(int)> TF;
void ff()
{
printf("in ff\n");
}
int main()
{
auto atf = std::tr1::bind(ff);
int i = 1;
atf(i);
}
在gdb下看下变量的类型
tsecer@harry: g++ pure.call.cpp -std=c++0x -g
tsecer@harry: gdb ./a.out
GNU gdb (GDB) Red Hat Enterprise Linux (7.2-50.el6)
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /data/home/harry/work/function.converting.constructor/a.out...done.
(gdb) b ff
Breakpoint 1 at 0x4005a8: file pure.call.cpp, line 7.
(gdb) r
Starting program: /data/home/harry/work/function.converting.constructor/a.out
warning: no loadable sections found in added symbol-file system-supplied DSO at 0x7ffff7ffa000
Breakpoint 1, ff () at pure.call.cpp:7
7 printf("in ff\n");
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.149.tl1.5.x86_64 libgcc-4.4.6-4.tl1.x86_64 libstdc++-4.4.6-4.tl1.x86_64
(gdb) bt
#0 ff () at pure.call.cpp:7
#1 0x000000000040071f in std::tr1::_Bind<void (*())()>::__call<int&>(const std::tr1::tuple<int&> &, std::tr1::_Index_tuple<>) (this=0x7fffffffe4a0, __args=...)
at /usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/tr1_impl/functional:1137
#2 0x0000000000400674 in std::tr1::_Bind<void (*())()>::operator()<int>(int &) (this=0x7fffffffe4a0, __args#0=@0x7fffffffe49c) at /usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/tr1_impl/functional:1191
#3 0x00000000004005f3 in main () at pure.call.cpp:14
(gdb) f 3
#3 0x00000000004005f3 in main () at pure.call.cpp:14
14 atf(i);
(gdb) ptype atf
type = struct std::tr1::_Bind<void (*())()> : public std::tr1::_Weak_result_type<void (*)()> {
private:
void (*_M_f)(void);
std::tr1::tuple<> _M_bound_args;
public:
void _Bind(void (*)(void));
void operator()<int>(int &);
private:
void __call<int&>(const std::tr1::tuple<int&> &, std::tr1::_Index_tuple<>);
}
(gdb) c
Continuing.
in ff
Program exited normally.
(gdb)
五、内存管理
1、对象的复制
在前面的例子中也看到了一个隐藏的问题,bind函数返回的是一个对象,在function执行构造函数的时候,需要将这个对象赋值一份,保存在自己的对象结构中;对于标准的函数,只需要保存这个函数指针就可以了。例如上面的例子中f函数只是一个代码段指针,所以保存下地址就可以了;而对于std::tr1::bind(vv)返回的内容,则是一个临时对象,如果function只是保留了这个对象的指针,明显保存的就是一个无效的内存地址。
如果需要特殊保存,下面的__functor._M_access<_Functor*>() = new _Functor(__f);语句通过new动态创建一个对象保存起来,从而避免函数返回的是临时变量的问题
gcc-4.1.0\libstdc++-v3\include\tr1\functional
class _Function_base
{
public:
static const std::size_t _M_max_size = sizeof(_Nocopy_types);
static const std::size_t _M_max_align = __alignof__(_Nocopy_types);
template<typename _Functor>
class _Base_manager
{
……
private:
static void
_M_init_functor(_Any_data& __functor, const _Functor& __f, true_type)
{
new (__functor._M_access()) _Functor(__f);
}
static void
_M_init_functor(_Any_data& __functor, const _Functor& __f, false_type)
{
__functor._M_access<_Functor*>() = new _Functor(__f);
}
};
2、对象的获取
这个地方将创建对象的地址返回,并且这个类型是模版声明时的静态类型
gcc-4.1.0\libstdc++-v3\include\tr1\functional
/**
* @if maint
* Base class of all polymorphic function object wrappers.
* @endif
*/
class _Function_base
{
……
// Retrieve a pointer to the function object
static _Functor* _M_get_pointer(const _Any_data& __source)
{
const _Functor* __ptr =
__stored_locally? &__source._M_access<_Functor>()
/* have stored a pointer */ : __source._M_access<_Functor*>();
return const_cast<_Functor*>(__ptr);
}
3、对象的调用
当有了functor类型的指针之后,就可以通过该指针直接调用对应的functor接口了
gcc-4.1.0\libstdc++-v3\include\tr1\functional_iterate.h
template<typename _Res _GLIBCXX_COMMA _GLIBCXX_TEMPLATE_PARAMS>
_Res
function<_Res(_GLIBCXX_TEMPLATE_ARGS)>::operator()(_GLIBCXX_PARAMS) const
{
if (_M_empty())
{
#if __EXCEPTIONS
throw bad_function_call();
#else
std::abort();
#endif
}
return _M_invoker(_M_functor _GLIBCXX_COMMA _GLIBCXX_ARGS);
}
在函数_M_invoke中,其中再次调用_M_get_pointer接口返回真正的执行functor
template<typename _Functor _GLIBCXX_COMMA _GLIBCXX_TEMPLATE_PARAMS>
class _Function_handler<void(_GLIBCXX_TEMPLATE_ARGS), _Functor>
: public _Function_base::_Base_manager<_Functor>
{
typedef _Function_base::_Base_manager<_Functor> _Base;
public:
static void
_M_invoke(const _Any_data& __functor _GLIBCXX_COMMA _GLIBCXX_PARAMS)
{
(*_Base::_M_get_pointer(__functor))(_GLIBCXX_ARGS);
}
};
4、对象的销毁
也就是在析构函数中完成对象占用空间的释放
gcc-4.1.0\libstdc++-v3\include\tr1\functional
class _Function_base
{
public:
static const std::size_t _M_max_size = sizeof(_Nocopy_types);
static const std::size_t _M_max_align = __alignof__(_Nocopy_types);
template<typename _Functor>
class _Base_manager
{
……
// Destroying an object located on the heap.
static void
_M_destroy(_Any_data& __victim, false_type)
{
delete __victim._M_access<_Functor*>();
}
static bool
_M_manager(_Any_data& __dest, const _Any_data& __source,
_Manager_operation __op)
{
switch (__op) {
case __get_type_info:
__dest._M_access<const type_info*>() = &typeid(_Functor);
break;
case __get_functor_ptr:
__dest._M_access<_Functor*>() = _M_get_pointer(__source);
break;
case __clone_functor:
_M_clone(__dest, __source, _Local_storage());
break;
case __destroy_functor:
_M_destroy(__dest, _Local_storage());
break;
}
return false;
}
…………
~_Function_base()
{
if (_M_manager)
{
_M_manager(_M_functor, _M_functor, __destroy_functor);
}
}
bool _M_empty() const { return !_M_manager; }
typedef bool (*_Manager_type)(_Any_data&, const _Any_data&,
_Manager_operation);
_Any_data _M_functor;
_Manager_type _M_manager;
};