这个问题,我困扰了好一会,决定记录一下。
一、问题引出以及分析
问题代码简化如下:
class B;编译出错提示信息:
class A
{
friend class B;
private:
A() = default;
bool operator()(int lhs,int rhs){ return lhs<rhs; }
};
class B
{
//...working
add_item(int times){ pq.push(times); }
private:
std::priority_queue<int,std::vector<int> ,A> pq;
};
'bool A::operator()(int, int)' is private
你知道问题出现在哪里吗??如果你看出来了,那么恭喜你,我是花了挺长时间纠结的。
我的理解是这样的。 类A有一个删除的默认构造函数,类的用户是没有权限新建实例的,主要是为了封装数据。我一开始以为,B是A的友元类,理论上在B的作用域中可以建立A的对象,访问A的私有接口。 这样想确实没有错误,问题出现在我是在类B的成员std::priority_queue中使用类A。 友元不具有传递性和继承性,只能在B类的作用域中可以使用,相当于在B的成员函数中使用。但是std::priority_queue中的成员不具备访问类A的权限。除非将std::priority_queue声明为类A的友元类。因此定义如下函数便报错。
add_item(int times){ pq.push(times); }因为在pq的成员中使用了类A的成员函数, 每次向pq插入一个元素便会调用这个类A这个成员,如下
bool operator()(int lhs,int rhs){ return lhs<rhs; }
二、问题解决
1、很自然会想到将类A的成员权限设置为public。 这样所有类A的用户都能访问,确实能解决问题。但是这样不能对数据很好的封装。因为类A只是一个辅助类。2、类A只是作为一个函数对象存在而已,有没有别的办法代替类A? 答案是有,使用其他函数对象(函数,函数指针,lambda表达式,重载了函数调用运算符的类),这里lambda表达式代替A, 但是这里有一个小问题。lambda是一个函数对象实例而不是一种类型(type), 而std::priority_queue接收的是需要一个类型。 这需要一个转化。
template<
class T,
class Container = std::vector<T>,
class Compare = std::less<typename Container::value_type>
> class priority_queue;
可能会写出如下代码:
class B这也是行不通的,类在编译的时候只看声明,auto需要根据给出的lambda 表达式推断出对应的类型,因此成员Less出错,当然pq声明也就出错了。
{
//...working
add_item(int times){ pq.push(times); }
private:
auto Less = [](int& lhs,int rhs)->bool {return lhs<rhs;}
std::priority_queue<int,std::vector<int> ,decltype(Less)> pq;
};
3、接下来就开始思考如何应对这个问题。查看std::priority_queue的constructor. 自定义一个函数比较器类型。
explicit priority_queue( const Compare& compare = Compare(),
const Container& cont = Container() );(until C++11)
priority_queue( const Compare& compare, const Container& cont );(since C++11)
//...省略
代码如下: 使用简单函数指针作为类型。新建一个对象需要提供这个函数指针类型的一个实例。
#include <iostream>在C++11。 可以使用标准库提供的函数类型(std::function)
#include<stdio.h>
#include<queue>
#include<vector>
#include<functional>
class B
{
//...working
add_item(int times){ pq.push(times); }
private:
std::priority_queue<int,std::vector<int> ,auto(*)(const int& ,const int&)->bool >
pq{
[](const int& lhs, const int& rhs)->bool
{
return lhs < rhs;
}
};
};
class B
{
//...working
add_item(int times){ pq.push(times); }
private:
std::priority_queue<int,std::vector<int> ,std::function<bool(const int&,const int& )> >
pq{
[](const int& lhs, const int& rhs)->bool
{
return lhs < rhs;
}
};
};