2016-07-25 85 views
3

从李普曼等人C++引物第5版,部分16.1.2:朋友比较和在C++类模板的关系运算符

//forward declarations needed for friend declarations in Blob 
template <typename> class BlobPtr; 
template <typename> class Blob; 
template <typename T> bool operator==(const Blob<T>&, const Blob<T>&) 

template <typename T> class Blob { 
    friend class BlobPtr<T>; 
    friend bool operator==<T>(const Blob<T>&, const Blob<T>&); 
} 

第一个问题:在线路

friend bool operator==<T>(const Blob<T>&, const Blob<T>&); 

为什么在==之后出现<T>?为什么不简单地写

friend bool operator==(const Blob<T>&, const Blob<T>&); 

我添加了下面的代码来定义运算符==和实例化类模板。它成功地编译和链接:

template <typename T> 
bool operator==(const Blob<T> &lhs, const Blob<T> &rhs) {return true;} 

int main() { 
    Blob<int> a, b; 
    a == b; 
} 

如果我删除<T>在friend声明如下operator==,我得到一个链接错误:

Undefined symbols for architecture x86_64: "operator==(Blob<int> const&, Blob<int> const&)", referenced from: _main in partial_blob-3ccda9.o 

显然<T>以下operator==是必要的,但为什么呢?

第二个问题:如果我想定义比运营商<关系少为同一类,我想我应该遵循的==工作模式:

1)前瞻性声明的操作

2)申报经营者为友,在插入附加<T>其功能我不明白

3)定义了运营商外的类。

因此,我添加以下代码:

template <typename T> bool operator<(const Blob<T>&, const Blob<T>&); 
template <typename T> class Blob { 
    //other members as before 
    friend bool operator<<T>(const Blob<T>&, const Blob<T>&); 
} 
bool operator<(const Blob<T>&, const Blob<T>&) {return true;} 
int main() { 
    //other statements as before 
    a < b; 
} 

这将产生围绕operator<<T>编译错误,我想是因为编译器把<<作为插入运算符。但是,如果我重写朋友宣布为

friend bool operator<(const Blob<T>&, const Blob<T>&); 

然后我得到==类似于早期的链接错误链接错误:

"operator<(Blob<int> const&, Blob<int> const&)", referenced from: _main in partial_blob-a85d5d.o 

我怎样才能成功地定义操作<这个类?

(注:因为更完全实现实现依赖于私有变量的运营商必须声明为朋友)

+1

请记住,像这样声明的朋友函数不是成员函数,它们是非成员函数,没有''函数不完整。至于'运营商<'尝试在运营商名称和模板(如运营商< )之间添加一个空格。 –

+0

关于未定义的参考及其原因和解决方案(如您所知); http://stackoverflow.com/a/35891188/3747990 – Niall

+0

@JoachimPileborg - 你能提供一个对这个语法介绍的参考吗?我甚至不知道它叫什么,所以我很难查找它。它是模板专业化吗?显式实例化?对这些术语进行网络搜索不会产生有用的结果。 – Chad

回答

0

我发布了自己的答案,承认Joachim Pileborg和songyuanyao的方向。

我简化了代码,只专注于问题1。 Pileborg和Holt正确地指出,重载<只需要一个空间来帮助编译器解析。

template <typename> class Blob; 
template <typename T> bool operator==(const Blob<T>&, const Blob<T>&); //line 2 

template <typename T> class Blob { 
    friend bool operator==(const Blob<T>&, const Blob<T>&); //line 5 
}; 

template <typename T> 
bool operator==(const Blob<T> &lhs, const Blob<T> &rhs) {return true;} //line 9 

int main() { 
    Blob<int> a, b; //line 12 
    a == b; //line 13 
} 

此代码在链接时产生错误。为了理解为什么,我们将从标准看相关语言。

从C++ 14标准n4296,14.5.4(请参阅底部以获取此处使用的术语摘要)。

For a friend function declaration that is not a template declaration:

(1.1) — if the name of the friend is a qualified or unqualified template-id, the friend declaration refers to a specialization of a function template, otherwise,

(1.2) — if the name of the friend is a qualified-id and a matching non-template function is found in the specified class or namespace, the friend declaration refers to that function, otherwise,

(1.3) — if the name of the friend is a qualified-id and a matching function template is found in the specified class or namespace, the friend declaration refers to the deduced specialization of that function template (14.8.2.6), otherwise,

(1.4) — the name shall be an unqualified-id that declares (or redeclares) a non-template function.

现在我们来看看第5行的朋友声明,确定它是指根据上面列出的四个步骤。

(1.1)==不是模板标识;继续...

(1.2)==不是合格ID;继续...

(1.3)==不是合格的ID;继续...

(1.4)因此,==是声明(或重新声明)非模板函数的非限定id。

根据该标准的第7.3.3节,朋友==在最内层的封闭名称空间中声明 - 在本例中是全局名称空间。

当我们在第12行实例化Blob<int>时,编译器通过用int替代类Blob中的所有出现的T来生成源代码。在编译器生成的代码的朋友声明,然后写道:

friend bool operator==(const Blob<int>&, const Blob<int>&); 

因此,我们已经宣布的operator==在全局命名空间(非模板)过载,与const Blob<int>&类型的参数。

当在第12行调用a == b时,编译器开始重载解析过程。它首先查找与参数类型相匹配的任何非模板重载。当Blob<int>被实例化时,它找到operator==形式的完美匹配。链接器然后查找对应于最佳匹配声明的operator==的定义,但它找不到任何内容,因为operator==(const Blob<int>&, const Blob<int>&)从未实际定义过!

的解决方案是使用模板id(这是不是一个模板声明)作为友元声明名称:

friend bool operator== <>(const Blob<T>&, const Blob<T>&) 

friend bool operator== <T>(const Blob<T>&, const Blob<T>&) 

两个operator== <>operator== <T>是模板-id(参见下面的术语);前者具有从函数参数列表推导出的隐式模板参数列表,后者具有明确的模板参数列表。

Blob<int>上线12实例化,编译器生成以下代码为朋友的声明:

friend bool operator== <>(const Blob<int>&, const Blob<int>&) 

friend bool operator== <int>(const Blob<int>&, const Blob<int>&) 

在任一情况下,朋友的名字是不合格template-id,所以在上面(1.1)中,朋友声明指的是功能模板的专门化。编译器然后找到,然后找到所需的<int>专业化的最佳模板匹配。它找到的唯一模板是在第2行中声明的模板,并在第9行中定义,它根据需要调用它。

术语

合格-ID:具有附接的作用域运算符的标识符,例如std::string::i

unqualified-id:没有附加范围操作符的标识符,例如, stringi

模板id:以下摘录(从C++ 14标准n4296,14.2)总结模板id的结构:

因此,一些模板id的将包括Foo<T>==<T>。 (==是一个operator-function-id)。请注意,与模板声明不同,template <>未包含在模板标识表达式中。

2

why is the <T> present after == ? Clearly the <T> following operator== is necessary, but why?

因为operator==在朋友的声明是指函数模板,你必须明确地指定它。否则,将会声明一个非模板函数,但是它的定义不能在以后找到。它与调用(和实例化)函数模板不一样。

注意T可以省略,但仍需要<>。如:

// refers to a full specialization of operator== 
friend bool operator== <>(const Blob<T>&, const Blob<T>&); 

另一名候选人的办法是定义类的声明,这将是内联,并可能被宣布为无模板函数内部操作。如:

template <typename T> class Blob { 
    ... 
    friend bool operator==(const Blob&, const Blob&) { 
     return ...; 
    } 
} 

This produces a compilation error around operator<<T>

是如你所说,应该写为friend bool operator< <T>(...),或friend bool operator< <>(...),或者看到我对非模板函数朋友的建议。

0

First question: in the line

friend bool operator==<T>(const Blob<T>&, const Blob<T>&); 

why is the <T> present after == ? Why not simply write

friend bool operator==(const Blob<T>&, const Blob<T>&); 

如果删除<T>,GCC发出警告:

warning: friend declaration ' bool operator==(const Blob< <template-parameter-1-1> >&, const Blob< <template-parameter-1-1> >&) ' declares a non-template function [-Wnon-template-friend]

你正在一个非模板函数类的朋友,所以编译器/连接器将寻找一个非模板功能,在你的情况下:

bool operator==(const Blob<int>&, const Blob<int>&); 

......它不存在,因此链接器找不到它。

如果您未将<T>(或<>添加到friend声明中),则必须为每种类型定义一个函数,这可能不是您想要的。

Second question: If I want to define the relational less than operator < for the same class, I would guess that I should follow the pattern that worked for ==:

这是一个简单问题的方式的C++代码解析,您需要operator<<<之间插入一个空格。这与C++ 11之前存在的问题相同,因为>>因为必须使用vector<vector<int> >而不是vector<vector<int>>