函数的二义性与函数对象的传递问题(通过实现vector的to_string示例)

许多时候,我们想要直接打印容器的内容,比如

std::vector<int> a = { 1, 2, 3 };

可以打印出[1, 2, 3]。

 

参考标准库,可以写出一个带有迭代器的to_string函数:

template <typename Iter, typename Func>
std::string to_string(Iter begin, Iter end, Func func) {std::string str;str.append("[");if (begin != end) {str.append(func(*begin++));while (begin != end) {str.append(", ");str.append(func(*begin++));}}str.append("]");return str;
}

在调用时候,可以直接传入begin()和end(),作为迭代器,再传入对于元素的to_string函数,即可实现容器的to_string。

std::to_string是C++11新增的标准库中定义的函数,具有多个重载函数(详情请参考to_string - C++Reference):

string to_string (int val);
string to_string (long val);
string to_string (long long val);
string to_string (unsigned val);
string to_string (unsigned long val);
string to_string (unsigned long long val);
string to_string (float val);
string to_string (double val);
string to_string (long double val);

由于是重载函数,因此不能直接使用名称to_string进行函数的传递。

我们想要使用类似于to_string(vec.begin(), vec.end(), to_string)这种传递方式,怎么办呢?

 

在C++11中,我们可以传递匿名函数(lambda)解决二义性问题:

用 [](const T &t) { return to_string(t); } 取代 to_string。

to_string(vec.begin(), vec.end(), [](const T &t) { return to_string(t); });

还有一种方法,就是定义函数对象:

template <typename T>
struct ToString
{std::string operator()(const T &t){return to_string(t);}
};

to_string(vec.begin(), vec.end(), ToString<T>());

这在C++11标准之前的代码中经常使用。

 

借此,我们可以定义一个vector版的to_string函数:

template <typename T>
std::string to_string(const std::vector<T> &vec)
{return to_string(vec.begin(), vec.end(),[](const T &t) { return to_string(t); });
}
// Or
template <typename T>
std::string to_string(const std::vector<T> &vec)
{return to_string(vec.begin(), vec.end(), ToString<T>());
}

然后,就可以这么使用了:

	std::vector<int> a = { 1, 2, 3 };std::vector<int> b = { 4, 5, 6 };std::vector<std::vector<int>> c = { a, b };std::cout << to_string(a) << std::endl; // [1, 2, 3]std::cout << to_string(b) << std::endl; // [4, 5, 6]std::cout << to_string(c) << std::endl; // [[1, 2, 3], [4, 5, 6]]

可以输出vector的各种类型(包括vector)。

 

然而,存在一个问题就是,由于我的这些代码在最初就using namespcae std;了,因此并没有什么问题出现(很好地实现了vector类型的to_string重载)。

但是,当我们把该行去掉后,就会发现,编译器会报出错误。

比如在ToString类的operator()函数中,在

return to_string(t);

这一行,就报出了:

error: no matching function for call to 'to_string(const int&)'

的错误。

似乎解决方法很简单,直接加std::不就行了吗?

然而,我们发现,这种解决方法,在只输出a和b的情况下,确实没什么问题,但是,当输出c的时候,又报出了

error: no matching function for call to 'to_string(const std::vector<int>&)'

的错误。

——怎么办呢?

其实解决起来很简单,就是使用using使得命名空间无效,当然这个using是加在函数里面的。

template <typename T>
struct ToString
{std::string operator()(const T &t){using std::to_string; // 标准库to_stringusing ::to_string;    // 自定义to_stringreturn to_string(t);}
};

using ::to_string表示你自定义to_string的位置,如果也是在命名空间里面,如(namespace A),那么就写成using A::to_string;即可。

 

完整代码如下:

#include <iostream>
#include <string>
#include <vector>template <typename Iter, typename Func>
std::string to_string(Iter begin, Iter end, Func func) {std::string str;str.append("[");if (begin != end) {str.append(func(*begin++));while (begin != end) {str.append(", ");str.append(func(*begin++));}}str.append("]");return str;
}template <typename T>
struct ToString;template <typename T>
std::string to_string(const std::vector<T> &vec)
{return to_string(vec.begin(), vec.end(), ToString<T>());// Or/*return to_string(vec.begin(), vec.end(),[](const T &t) {using std::to_string;using ::to_string;return to_string(t);});*/
}template <typename T>
struct ToString
{std::string operator()(const T &t){using std::to_string;using ::to_string;return to_string(t);}
};int main(void)
{std::vector<int> a = { 1, 2, 3 };std::vector<int> b = { 4, 5, 6 };std::vector<std::vector<int>> c = { a, b };std::cout << to_string(c) << std::endl;return 0;
}

 

转载于:https://www.cnblogs.com/chillmagic/p/5713773.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/458204.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

libSVM介绍(二)

from&#xff1a;https://blog.csdn.net/carson2005/article/details/6539192 鉴于libSVM中的readme文件有点长&#xff0c;而且&#xff0c;都是采用英文书写&#xff0c;这里&#xff0c;我把其中重要的内容提炼出来&#xff0c;并给出相应的例子来说明其用法&#xff0c;大家…

四则运算题2

本题新学知识点&#xff1a; itoa函数 char *itoa( int value, char *string,int radix);[1]原型说明&#xff1a;value&#xff1a;欲转换的数据。string&#xff1a;目标字符串的地址。radix&#xff1a;转换后的进制数&#xff0c;可以是10进制、16进制等。程序实例:#includ…

c++调用Libsvm

libSVM中的readme中文版&#xff1a;http://blog.csdn.net/carson2005/article/details/6539192 LibSVM的package中的Readme文件中介绍了怎样具体的使用LibSvm&#xff0c;可以在Dos下以命令形式进行调用&#xff0c;也可以用程序包中提供的GUI程序Svm-toy进行图形化的操作。sv…

STL -set

转载自&#xff1a;http://blog.csdn.net/LYHVOYAGE/article/details/22989659 set集合容器实现了红黑树&#xff08;Red-Black Tree&#xff09;的平衡二叉检索树的的数据结构&#xff0c; 在插入元素时&#xff0c;它会自动调整二叉树的排列&#xff0c;把该元素放到适当的位…

【机器学习实战之一】:C++实现K-近邻算法KNN

本文不对KNN算法做过多的理论上的解释&#xff0c;主要是针对问题&#xff0c;进行算法的设计和代码的注解。 KNN算法&#xff1a; 优点&#xff1a;精度高、对异常值不敏感、无数据输入假定。 缺点&#xff1a;计算复杂度高、空间复杂度高。 适用数据范围&#xff1a;数值…

libsvm C++ 代码参数说明汇总

几个重要的数据结构 2.1 struct svm_problem {int l; // 记录样本的总数double *y; // 样本所属的标签(1, -1)struct svm_node **x; // 指向样本数据的二维数组(即一个矩阵&#xff0c;行数是样本数&#xff0c;列数是特征向量维度) }; 2.2 struct svm_node {int …

javascript设计模式-继承

javascript继承分为两种&#xff1a;类式继承&#xff08;原型链、extend函数&#xff09;、原型式继承&#xff08;对继承而来的成员的读和写的不对等性、clone函数&#xff09;。 类式继承-->prototype继承&#xff1a; 1 function Person(name){2 this.name …

GIS基础软件及操作(二)

原文 GIS基础软件及操作(二) 练习二、管理地理空间数据库 1.利用ArcCatalog 管理地理空间数据库 2.在ArcMap中编辑属性数据 第1步 启动 ArcCatalog 打开一个地理数据库 当 ArcCatalog打开后&#xff0c;点击, 按钮&#xff08;连接到文件夹&#xff09;. 建立到包含练习数据的…

libSVM分类小例C++

from&#xff1a;http://www.doczj.com/list_31/ 使用libSVM求解分类问题的C小例 1.libSVM简介 训练模型的结构体 struct svm_problem//储存参加计算的所有样本 { int l; //记录样本总数 double *y; //指向样本类别的组数 //prob.y new double[prob.l]; struct svm_node …

qunit 前端脚本测试用例

首先引用qunit 测试框架文件 <link rel"stylesheet" href"qunit-1.22.0.css"> <script src"qunit-1.22.0.js"></script> <div id"qunit"></div> <div id"qunit-fixture"></div>…

非常规文件名删除

生活中我们偶尔会遇到这样一件事&#xff1a;走在路上&#xff0c;突然感觉鞋底有东西&#xff0c;抬脚一看&#xff0c;是个泡泡糖。拿不掉&#xff0c;走路还一粘一粘的。要多难受有多难受&#xff01;同样在linux中也有这么一种文件名。看着不舒服&#xff0c;却删不掉。今天…

Machine Learning(Stanford)| 斯坦福大学机(吴恩达)器学习笔记【汇总】

from&#xff1a;https://blog.csdn.net/m399498400/article/details/52556168 定义本课程常用符号 训练数据&#xff1a;机器用来学习的数据 测试数据&#xff1a;用来考察机器学习效果的数据&#xff0c;相当于考试。 m 训练样本的数量&#xff08;训练集的个数) x 输入的…

PHP OOP

类跟对象的关系类是对象的抽象(对象的描述(属性)&#xff0c;对象的行为(方法))对象是类的实体面相对象的三大特征&#xff1a;封装、集成、多态自定义类Class Person{}属性定义属性是类里面的成员&#xff0c;所以要定义属性的前提条件是需要声明一个类Class Person{public $n…

kv存储对抗关系型数据库

http://www.searchdatabase.com.cn/showcontent_52657.htm转载于:https://www.cnblogs.com/hexie/p/5276034.html

模板匹配算法

from&#xff1a;https://blog.csdn.net/zhi_neng_zhi_fu/article/details/51029864 模板匹配(Template Matching)算法 模板匹配&#xff08;Template Matching&#xff09;是图像识别中最具代表性的方法之一。它从待识别图像中提取若干特征向量与模板对应的特征向量进行比较…

关于linux用户权限的理解

创建用户useradd 用户名创建用户组groupadd 组名查看用户Idid 用户修改文件权限chmod 777 文件名或目录-R 递归修改用户数组chown 属主&#xff1a;属组 文件名或目录名-R 递归转载于:https://blog.51cto.com/1979431/1833512

IMEI串号

IMEI串号就是国际移动设备身份码&#xff0c;是电子设备的唯一身份证&#xff0c;由于它的唯一性&#xff0c;它可以用来查询电子设备的保修期还有产地&#xff0c;可以说用处直逼人民的身份证啊&#xff01; 在拨号键盘页面 输入【*#06#】五个字符转载于:https://www.cnblogs…

立体匹配十大概念综述---立体匹配算法介绍

from&#xff1a;https://blog.csdn.net/wintergeng/article/details/51049596 一、概念 立体匹配算法主要是通过建立一个能量代价函数&#xff0c;通过此能量代价函数最小化来估计像素点视差值。立体匹配算法的实质就是一个最优化求解问题&#xff0c;通过建立合理的能量函数…

zjnu1730 PIRAMIDA(字符串,模拟)

Description Sample Input 6 JANJETINA 5 1 J 1 A 6 N 6 I 5 E Sample Output 1 0 2 1 1题意&#xff1a;给你一个长度小于等于10^6的字符串&#xff0c;然后每次让它循环铺盖&#xff0c;构成层数为n的塔&#xff0c;让你求得第i层塔中某个字符的个数。 思路&#xff1a;首先要…

ICP算法理解

from&#xff1a;https://blog.csdn.net/linear_luo/article/details/52576082 1 经典ICP ICP的目的很简单&#xff0c;就是求解两堆点云之间的变换关系。怎么做呢&#xff1f;思路很自然&#xff0c;既然不知道R和t(针对刚体运动)&#xff0c;那我们就假设为未知量呗&#xf…