注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

从tr1中function使用看converting constructor  

2018-03-12 19:54:37|  分类: C/C++基础 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、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;
  };

  评论这张
 
阅读(50)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018