场景
在main函数中的两个参数(int argc , char *argv[])
,都是系统构造的。通常来说,我们只需要去解析即可,不需要去构造这样一个参数。
然而,今天写代码时却不得不构造这样一个参数。原因是使用了一个第三方的模块(准确的说是基于第三方模块),第三方模块初始化时需要接受(int argc , char ** &argv)
这样的参数。注意到其中的引用符号!
通常来说,直接传递main函数的argc
, argv
是没有问题的。事实也确实如此,传递main函数接受的argv
不会有编译错误。不过有些坑就是,这个模块自己参数解析写得太武断!就是不允许有多余的参数项!一旦有多余参数项就会导致其停止解析参数,最终导致其解析失败。然而我的程序除了给这个模块传参数,还需要额外的参数啊!
没有办法,我就得自己构造仅仅包含第三方模块需要的参数argv
, 但是狗血的事情出现了!
怎么构造就是不行!!编译一直报错:
用类型为‘char**’的右值初始化类型为‘char**&’的非常量引用无效
经过一系列的修改,终于正确编译了。
一切的狗血,都是因为概念理解的不清晰。
解决方案
只考虑argv
的构造:
int fack_main(int argc , char **&argv);
int fack_argc = 2 ;
char arg0[] = "program name" ;
char arg1[] = "arg1 for thirdparth" ;
char **fake_argv = new char *[fack_argc+1]{ arg0 , arg1 } ;
fack_main(fack_argc , fack_argv) ;
delete [] fack_argv ;
以上就是完整的构造过程。这样构造的fack_argv
应该与main
函数中的argv
是完全等价的。
解决过程
解决过程就很辛酸了。
- raw
fack_argv[2][100] { "program name" , "arg1" } ; fack_main(fack_argc , fack_argv) ;
报错:
用类型为‘char (*)[100]’的右值初始化类型为‘char**&’的非常量引用无效
看到这个错误还是很好理解的。说明参数传递时
fack_argv
还是是一个指向char[100]
的指针类型char (*)[100]
,不是char **&
。想到的解决办法是
static_cat
- static_cast
fack_argv[2][100] { "program name" , "arg1" } ; fack_main(fack_argc , static_cast<char **>(fack_argv)) ;
结果报错:
从类型‘char [3][100]’到类型‘char**’中的 static_cast 无效
为何强制转换会失效呢?
难道只能一步步转换?
试了以下代码:
fack_main(argc , static_cast<char **>(static_cast<char(*)[100]>(f_argv))) ;
想要通过两步完成,但是依然报错:
错误:从类型‘char (*)[100]’到类型‘char**’中的 static_cast 无效
这说明从一维数组到指针是OK的,但是第二步就失败了。一想,第二步时,代码其实是在将一个
指向数组的指针
转为一个指向指针的指针
。这或许是编译器不支持的。失败之后,再一想,既然直接声明二维数组失败了,那么看来只有声明一个一维的数组,数组里放
char *
就好了。 - 指向指针(char*)的数组
char arg0[] = "program name" ; char arg1[] = "arg1" ; char *fake_argv[2] = { arg0 , arg1 } ;
结果还是报错:
错误:用类型为‘char**’的右值初始化类型为‘char**&’的非常量引用无效
这次需要注意了,前面已经对了——我们已经构造了
char**
,无奈目标是char **&
。但是奇怪的是,传main的argv
就不会报错,那说明这二者间还是有差别的!需要继续改!
这次着眼于后边部分——”非常量引用”!
是不是说
char **
可以转换为常量引用
啊? - 测试:将
char *[]
转为常量引用鼓捣了半天,终于弄明白常量引用如何声明:
char *fake_argv[2] = { arg0 , arg1 } ; char ** const (&ra) = fake_argv ;
通过!
从上面可以看出,所谓
常量引用
,是要求一个底层const,就是说ra
的值不能改变。这个测试对问题而言帮助不大,但是隐式地说明——还是变量类型声明出了问题。
网上搜索!
- 网上搜索
argv *[]
查看 Defining own main functions arguments argc and argv
发现了问题是相似的,但是提供的解决办法依然不能解决形参是
car **&
的问题。不过收获到:argv最后要多一个NULL结束标志。
- 完全的类型一致!
既然是说
char **
到char **&
错误,但是我的实参依然是个数组,这说明编译器已经做了隐式转换。我记得在类的构造函数中说过,类的隐式转换最多一次。
有没有这种可能:
值参数到引用参数,其实也是一个隐式转换?
再加上前面的猜测——最多一次隐式转换,问题就明白了。一次隐式转换用来将
char *argv[]
变为char **argv
,所以就导致了char **
到char **&
的失败。书读得不多,我觉得应该会有相关介绍的。这里只是猜测。
这样,就有了最后的方案:
int fack_main(int argc , char **&argv); int fack_argc = 2 ; char arg0[] = "program name" ; char arg1[] = "arg1 for thirdparth" ; char **fake_argv = new char *[fack_argc+1]{ arg0 , arg1 } ; fack_main(fack_argc , fack_argv) ; delete [] fack_argv ;
通过编译!问题解决。