2

来自第 2 周的大师。我们有原来的功能:

string FindAddr( list<Employee> l, string name )
{
    for( list<Employee>::iterator i = l.begin(); // (1)
         i != l.end(); 
         i++ ) 
    {
        if( *i == name ) // (2)
        {
            return (*i).addr;
        }
    }
    return "";
}

我向其中添加了虚拟 Employee 类:

class Employee
{
    string n;
public:
    string addr;

    Employee(string name) : n(name) {}
    Employee() {}

    string name() const
    {
        return n;
    }

    operator string()
    {
        return n;
    }
};

并得到编译错误:

到位(1):

conversion from ‘std::_List_const_iterator<Employee>’ to non-scalar type ‘std::_List_iterator<Employee>’ requested

到位(2):

no match for ‘operator==’ in ‘i.std::_List_iterator<_Tp>::operator* [with _Tp = Employee]() == name’

为了消除第一个,我们iterator改为const_iterator。消除第二个错误的唯一方法是编写自己的运算符==。然而,赫伯萨特写道:

Employee 类未显示,但要使其工作,它必须具有到 string 的转换或采用 string 的转换 ctor

但是 Employee 有一个转换函数和转换构造函数。GCC 版本 4.4.3。正常编译,g++ file.cpp没有任何标志。

应该有隐式转换,它应该工作,为什么不呢?我不想要 operator==,我只希望它像 Sutter 所说的那样工作,转换为 string 或转换 ctor 采用 string

4

5 回答 5

3

Herb Sutter 在这种情况下是错误的(我没有“Exceptional C++”的副本,但我希望该 GotW 条目能够为本书清理。)

但首先,为了解决相关错误,您必须constl参数声明中删除 。(请注意,替换iteratorconst_iterator只会混淆问题:您的operator string()is not const,这意味着它不可用于常量 object *i)。

一旦你解决了第一个问题,你的代码确实无法编译

if( *i == name )

线。这是因为std::operator ==比较std::string对象的函数实际上被标准库定义为模板函数

template<class charT, class traits, class Allocator>
bool operator==(
  const basic_string<charT,traits,Allocator>& lhs,
  const basic_string<charT,traits,Allocator>& rhs);

为了让这个函数参与重载决议,它的模板参数必须被成功推导出来。这在您的上下文中是不可能的,因为在*i == name一个参数中是std::string,另一个是Employee。模板参数推导失败,因此,此模板函数不考虑用于重载解析。没有其他候选者,编译器会报告错误。

出于这个原因,Herb Sutter 声称代码应该operator string()在类中存在转换函数的情况下可编译的说法Employee是不正确的。代码可能会与标准库的某些特定实现一起编译,该实现为 声明一个专用的非模板比较运算符std::string,但通常标准库实现不会那样做。

他还提出了另一个毫无根据的主张,坚持认为这种转换的结果必须是暂时的。实际上,Employee类可以具有operator const string &() const转换功能,这不会创建临时对象(而是返回对数据成员的引用,就像您的示例中所做的那样)。

最后,他声称转换构造函数将使这段代码工作的说法只有在程序声明一个专用operator ==Employee vs. Employee比较的情况下才是正确的。如果不引入这样的专用运算符,转换构造函数将不会影响这段代码的有效性。即在您的示例中,声明Employee(string name)构造函数没有任何意义——它没有实现任何目标。

于 2012-11-07T22:53:56.840 回答
3

我相信代码可以编译,但不是必须的。请记住,这string是一个 typedef basic_string<char>,它本身不是一个类。最小的例子:

template <typename T>
struct basic_string
{
};

typedef basic_string<char> string;

struct Employee
{
    operator string() const;
};

template <typename T>
bool operator==(const basic_string<T>& a, const basic_string<T>& b);

// Compilation succeeds when this is uncommented
// bool operator==(const basic_string<char>& a, const basic_string<char>& b);

bool f(const Employee& e, const string& s)
{
    return e == s; // error
}

是的,Employee可转换为string,但它并不是真正的 a string,因此这不足以确定用于 的模板参数operator==

如果添加了一个额外的重载operator==(const string&, const string&),它就可以工作,另一个标准库实现可能会提供这个重载。如果提供,代码将编译,但它是标准 C++ 不需要的扩展。

编辑:实际上,正如其他人所提到的,这还不够(const问题),但即使解决了其他问题,这仍然存在,我相信这是您核心问题的答案。

于 2012-11-07T23:02:42.557 回答
1

免责声明:在我撰写并发布此问题时,问题的代码已经更改。该代码不再是 GOTW 代码的引用。这是不同的代码,所谓的编译错误显然也不正确,但我让这个答案(对原始帖子)保持不变,因为它主要涉及其他问题(我不打算通过相应的编辑来对问题进行一系列编辑并修改这个答案)。

@Vaibhav已经回答了核心问题,即需要明确表达转换。

但是由于引用的 GOTW(本周大师)涉及不必要的临时人员,您的课程Employee代码,

class Employee
{
    string n;
public:
    string addr;

    Employee(string name) : n(name) {}
    Employee() {}

    string name() const
    {
        return n;
    }

    operator string()
    {
        return n;
    }

};

重复该 GOTW 中讨论的一些陷阱。

Employee(string name) : n(name) {}

通过值获取字符串参数对于 C++11 来说很好,因为无论如何都会创建一个副本。但是你应该move把这个值放入成员中,

Employee(string name) : n(move(name)) {}

那么,你的

operator string()
{
    return n;
}

遭受 not being const,因此不能在const对象上调用它,因此为了调用此运算符,必​​须复制该对象。

所以,从技术上讲,做

operator string() const
{
    return n;
}

但在设计层面上,这也是错误的。员工不是字符串。希望员工转换为哪个字符串?他或她的名字?员工代码?社会安全号码?

隐式转换通常很麻烦,这就是一个例子。没有帮助explicit。由于相关字符串已经可以通过命名操作获得(这是一件好事),因此只需删除此转换运算符即可改进类。

于 2012-11-07T23:00:13.367 回答
0

您从迭代器收到一个 Employee 对象,因此 *i 指向一个 Employee 对象。然后,您需要指向该员工的姓名。

试试这个:

if( (*i).name == name )
于 2012-11-07T22:45:34.453 回答
-1

当您反转比较运算符的操作数时会发生什么?编译器抱怨没有operator ==for List<>,但它定义了 for string

更改if( *i == name )if( name == *i )应该工作。

于 2012-11-07T22:54:05.040 回答