前言
PCL中的surface/include/pcl/surface/mls.h 裡宣告的pcl::MovingLeastSquares 類別有這麼一段代碼:
using KdTree = pcl::search::Search<PointInT>;
using KdTreePtr = typename KdTree::Ptr;
using NormalCloud = pcl::PointCloud<pcl::Normal>;
using NormalCloudPtr = NormalCloud::Ptr;
using PointCloudOut = pcl::PointCloud<PointOutT>;
using PointCloudOutPtr = typename PointCloudOut::Ptr;
using PointCloudOutConstPtr = typename PointCloudOut::ConstPtr;
using PointCloudIn = pcl::PointCloud<PointInT>;
using PointCloudInPtr = typename PointCloudIn::Ptr;
using PointCloudInConstPtr = typename PointCloudIn::ConstPtr;
using SearchMethod = std::function<int (pcl::index_t, double, pcl::Indices &, std::vector<float> &)>;
觀察KdTree 和KdTreePtr ,可以發現一個沒加,另一個則有加typename 。他們之間的區別詳見下文。
KdTreePtr
KdTree::Ptr 簡化前為pcl::search::Search<PointInT>::Ptr ,因為它具體是什麼要依賴於PointInT ,在編譯時並不知道它是一個類別,所以在這裡加上typename 讓編譯器知道。
參考C++ Cast Template,如果我們要調用cast 對pcl::search::Search<PointInT>::Ptr 做型別轉換,編譯器在不知道pcl::search::Search<PointInT>::Ptr 是一個類型的情況下,可能會把cast 當成它的成員變量,因而導致編譯錯誤。所以這裡加上typename 讓編譯器知道它是一個類別後,就能成功編譯了。
KdTree
那麼為什麼KdTree 不用加typename 呢?參考Where and why do I have to put the “template” and “typename” keywords?:
There are many names for which typename is not necessary, because the compiler can, with the applicable name lookup in the template definition, figure out how to parse a construct itself - for example with T *f;, when T is a type template parameter. But for t::x * f; to be a declaration, it must be written as typename t::x *f;.
我們可以用模板參數T 直接去定義一個變量,但是如果想要用T::x 定義一個變量卻是行不通的(編譯器會把x 當作T 類型的成員變量),必須要在T::x 之前加上typename ,讓編譯器知道它是一個類型才行。
The syntax allows typename only before qualified names - it is therefore taken as granted that unqualified names are always known to refer to types if they do so.
typename 關鍵字只能加在qualified names(可以想成有帶::的類別名稱)之前,unqualified names總是會被當成類別名稱,所以不需要加typename 關鍵字。
來看一下qualified name的定義,參考Qualified name lookup:
A qualified name is a name that appears on the right hand side of the scope resolution operator :: (see also qualified identifiers). A qualified name may refer to a
class member (including static and non-static functions, types, templates, etc)
namespace member (including another namespace)
enumerator
它將qualified name的定義說得很明白,也就是:: 運算符後面的名稱。qualified name可以是一個類別的成員,命名空間的成員,或是enum。
在這邊的例子中,將模板參數PointT 代入pcl::search::Search<PointInT> 後,編譯器便可以很容易地知道它是一個類型(對照上面的T* f ),所以不需要加typename 。
如果我們想用pcl::search::Search<PointInT>::Ptr 定義一個變數,因為它是一個qualified name(對照上面的T::x* f ),所以必須在它之前加上typename 。
範例程序
以下程序展示typename 的使用時機:
#include <iostream>
#include <memory>
template<typename T>
class Animal{
public:
using Ptr = std::shared_ptr< Animal< T > >;
};
template<typename T>
void f(){
Animal<T> a;
typename Animal<T>::Ptr ap;
}
int main(){
Animal<int> a;
Animal<int>::Ptr ap;
return 0;
}
首先定義一個模板類別Animal ,裡面有個成員Ptr 。
在main 函數中,因為我們給定了Animal 的模板參數,所以編譯器知道他們兩個都代表一種類型,可以成功編譯。
在f 函數中,我們可以用Animal<T> 定義一個變數a ,但是卻無法用Animal<T>::Ptr 去定義變數ap ,必須在之前加上typename 才行。這是因為在T 不確定的情況下,編譯器把Ptr 當成Animal<T> 類別的成員變數而不把它視為一個類型,因此無法用它去定義其他變數。
以上程序放在typename_keyword.cpp。
|