4

作为学习练习,我一直在研究 C++ 中的自动类型转换是如何工作的。我知道通常应该避免自动类型转换,但我想通过了解它的工作原理来增加我对 C++ 的了解。

我创建了一个StdStringConverter可以自动转换为 a 的类std::string,但是当对象与真实对象进行比较时,编译器(Debian 上的 g++ 4.3.4)似乎没有进行转换std::string(请忽略缺少传递引用和不必要的临时对象创建):

#include <string>

class StdStringConverter
{
public:
    explicit StdStringConverter(std::string name) : m_name(name) {}
    operator const std::string () const { return m_name; }
private:
    std::string m_name;
};

int main()
{
    StdStringConverter converter(std::string("Me"));
    const std::string name = "Me";
    // Next line causes compiler error:
    // no match for 'operator==' in 'converter == name'
    return (converter == name) ? 0 : 1;
}

另一方面,如果我将其稍微更改为一个CStringConverter类,则确实会发生自动转换,尽管比较char指针可能不是我想要的:

#include <string>

class CStringConverter
{
public:
    explicit CStringConverter(std::string name) : m_name(name) {}
    operator const char* () const { return m_name.c_str(); }
private:
    std::string m_name;
};

int main()
{
    CStringConverter converter(std::string("Me"));
    const char* name = "Me";
    // Next line compiles fine, but they are not equal because the
    // pointers don't match.
    return (converter == name) ? 0 : 1;
}

std::string在这种情况下, a和 a之间的区别是否有什么特别之处char*,使得编译器不会将它们视为相同?

4

3 回答 3

7

问题是由于 std::string 实际上是类模板 std::basic_string 的一个实例。命名空间 std 中可用的 operator== 采用两个 std::basic_string 模板:


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

如果此版本的 operator== 专门在 std::string 上重载,那么您的代码就可以了。但事实并非如此,这将要求编译器对 std::basic_string 的模板参数执行模板参数推导,以便它可以理解您的转换运算符的返回是一个可能的匹配项。

但是,编译器不会这样做。我不知道标准的哪一部分准确地说明了这一点。但总体思路是,此类转换仅适用于非模板类型。

我可以建议您将 StdStringConverter 放在命名空间中,并为该命名空间中的 std::string 提供一个 operator== 版本。这样,当您的编译器发现像 ADL(参数相关查找)这样的表达式时,一切正常。


#include <string>

namespace n1 {

class StdStringConverter
{
public:
    explicit StdStringConverter(std::string name) : m_name(name) {}
    operator std::string () { return m_name; }
private:
    std::string m_name;
};

bool operator==(std::string const& a, std::string const& b)
{
  return a == b; //EDIT: See Paul's comment on std::operator== here.
}

}

int main()
{
    using namespace n1;
    StdStringConverter converter(std::string("Me"));
    std::string name = "Me";
    return (converter == name) ? 0 : 1;   
}
于 2009-09-18T12:36:36.540 回答
1

在第一个示例中,两个比较的类(string 和 StdStringConverter)没有从编译器获得任何特殊的类型转换处理。这意味着您所做的运算符重载甚至不会被触发。编译器查看 operator== 重载列表,其中没有一个接收 StdStringConverter ,因此它对你大喊大叫。

在第二个示例中,名称是 char *。由于它是原始类型,因此编译器会尝试将非原始类型转换为 char *。由于您有一个覆盖,它可以工作并且您比较地址。

编译器不会对不包含原始类型的操作进行显式类型转换。它将尝试使用构造函数来使类型匹配。例如,如果您将第一个示例更改为:

#include <string>

class StdStringConverter
{
public:
    StdStringConverter(std::string name) : m_name(name) {}
    bool operator==(const StdStringConverter &name) { return m_name == name.m_name; }
    operator const std::string () const { return m_name; }
private:
    std::string m_name;
};

int main()
{
    StdStringConverter converter(std::string("Me"));
    const std::string name = "Me";
    // Next line causes compiler error:
    // no match for 'operator==' in 'converter == name'
    return (converter == name) ? 0 : 1;
}

现在程序返回 0。由于构造函数现在不是显式的,编译器将尝试使用它将字符串转换为 StdStringConverter。由于现在在 StdStringConverter 中有一个 operator== 一切正常。

于 2009-09-18T11:59:33.453 回答
-1

有多种因素。如果您因此更改 return 语句

返回 (std::operator==(name, name)) ?0:1;

它编译,虽然它显然不做同样的事情。另一方面

return (std::operator==(converter, name)) ?0:1;

不但是提供更有趣的错误信息

调用 'operator==(StdStringConverter&, const std::string&) 没有匹配的函数

这提醒我 operator== 是在 basic_string<> 上模板化的,它有三个模板参数可以引导。如果您在示例中使用 int 而不是 std::string,编译器不会抱怨。

如何用 std::string 获得想要的效果更耐人寻味……

于 2009-09-18T11:58:28.760 回答