前言
PCL中的surface/include/pcl/surface/mls.h
裡宣告的pcl::MovingLeastSquares
類別有這麼一段代碼:
namespace pcl
{
template <typename PointInT, typename PointOutT>
class MovingLeastSquares : public CloudSurfaceProcessing<PointInT, PointOutT>
{
public:
//...
using PCLBase<PointInT>::input_;
using PCLBase<PointInT>::indices_;
using PCLBase<PointInT>::fake_indices_;
using PCLBase<PointInT>::initCompute;
using PCLBase<PointInT>::deinitCompute;
//...
};
}
這邊的using
有:
-
繼承共同行為
-
改變成員的訪問權限
兩種作用,詳見下文。
繼承關係
要看懂這邊這麼寫的用意,得先了解一下MovingLeastSquares
類別的來歷,以下是這個類別的繼承關係:MovingLeastSquares
→
\rarr
→CloudSurfaceProcessing
→
\rarr
→PCLBase
。
在PCLBase
類別中有protected成員變數input_
,indices_
,fake_indices_
及public成員函數initCompute
,deinitCompute
。
namespace pcl
{
//...
template <typename PointT>
class PCLBase
{
public:
//...
protected:
/** \brief The input point cloud dataset. */
PointCloudConstPtr input_;
/** \brief A pointer to the vector of point indices to use. */
IndicesPtr indices_;
/** \brief Set to true if point indices are used. */
bool use_indices_;
/** \brief If no set of indices are given, we construct a set of fake indices that mimic the input PointCloud. */
bool fake_indices_;
/** \brief This method should get called before starting the actual computation.
*
* Internally, initCompute() does the following:
* - checks if an input dataset is given, and returns false otherwise
* - checks whether a set of input indices has been given. Returns true if yes.
* - if no input indices have been given, a fake set is created, which will be used until:
* - either a new set is given via setIndices(), or
* - a new cloud is given that has a different set of points. This will trigger an update on the set of fake indices
*/
bool
initCompute ();
/** \brief This method should get called after finishing the actual computation.
*/
bool
deinitCompute ();
public:
PCL_MAKE_ALIGNED_OPERATOR_NEW
};
CloudSurfaceProcessing
類別繼承自PCLBase
,它有public成員變數input_
,indices_
以及public成員函數initCompute
,deinitCompute
。
namespace pcl
{
//...
template <typename PointInT, typename PointOutT>
class CloudSurfaceProcessing : public PCLBase<PointInT>
{
public:
//...
using PCLBase<PointInT>::input_;
using PCLBase<PointInT>::indices_;
using PCLBase<PointInT>::initCompute;
using PCLBase<PointInT>::deinitCompute;
//...
};
}
讓變數/函數變成dependent name
參考Why do I have to access template base class members through the this pointer?(巨著,有條件的最好都看一下這篇回答):
the standard in effect says that dependent base classes of class templates just aren't considered for search unless explicitly requested. This stops everything from being dependent just because it could be found in a dependent base. It also has the undesirable effect that you're seeing - you have to qualify stuff from the base class or it's not found
編譯器在編譯一個derived class template時,不會去尋找base class template。
看看CloudSurfaceProcessing
這個例子,它是一個繼承自PCLBase<PointInT>
的derivated class template。在默認情況下,編譯器如果看到CloudSurfaceProcessing
的代碼中用了input_
,並不會去它的base class template,也就是PCLBase<PointInT>
中查找,所以input_
在這種情況下並非dependent name。
編譯器會直接把它當成一個未宣告的變數,造成以下編譯錯誤:
Error C2065 'transformation_epsilon_': undeclared identifier
或:
Error C3861 'transformation_epsilon_': identifier not found
There are three common ways to make A dependent:
using Bar<T>::A; in the class - A now refers to something in Bar<T>, hence dependent.
Bar<T>::A *x = 0; at point of use - Again, A is definitely in Bar<T>. This is multiplication since typename wasn't used, so possibly a bad example, but we'll have to wait until instantiation to find out whether operator*(Bar<T>::A, x) returns an rvalue. Who knows, maybe it does...
this->A; at point of use - A is a member, so if it's not in Foo, it must be in the base class, again the standard says this makes it dependent.
要讓input_
變成dependent name(即讓編譯器知道它是繼承而來)有以下三種方法:
this->A;
int x = Bar<T>::A; // 假設Bar<T>::A是int型別
using Bar<T>::A;
第一種是每次用A
時都要在前面加this->
,這顯然不是一個好方法。
第二種是則是在A
前面加Bar<T>::A
,比第一種方法更麻煩。
第三種則是一勞永逸的做法:寫一次using Bar<T>::A;
,之後在任何地方都可以直接用A
而不造成編譯錯誤。
繼承共同行為
參考繼承共同行為。
MovingLeastSquares
和PCLBase
這兩個類別都有input_
, indices_
, fake_indices_
, initCompute
和deinitCompute
,這是他們的“共同行為”。
這裡的using指定了MovingLeastSquares
要使用PCLBase
中的,而非CloudSurfaceProcessing
中的。
改變成員的訪問權限
參考使用 using。
在PCLBase
中,這些成員變量/函數本來是protected。這種寫法同時也將繼承的成員導入到public權限區塊,
代表input_
, indices_
, fake_indices_
, initCompute
和deinitCompute
都變為MovingLeastSquares
的public成員。
範例程序
#include <iostream>
class Shape{
protected:
float area;
public:
void printArea() {
std::cout << "I'm Shape with area = " << area << std::endl;
}
};
//note: public inheritance
class Rectangle : public Shape{
protected:
using Shape::area;
public:
void printArea() {
std::cout << "I'm Rectangle with area = " << area << std::endl;
}
};
class Square : public Rectangle{
public:
using Shape::area;
//note: no () after function name
using Shape::printArea;
};
int main(){
Square sqr;
//"area" becomes a public member of "Square"
sqr.area = 9;
//it uses "printArea" from "Shape"
sqr.printArea();
//I'm Shape with area = 9
return 0;
}
在上面的例子中,可以看出:
- 在main函數裡面可以直接修改
Square
物件的area
成員函數,可見area
已經由protected變為public sqr.printArea()
函數的行為與Shape
中定義的printArea
一致。這代表Square
的printArea
是繼承自Shape
而非Rectangle
以上程序放在using_keyword.cpp。