【问题标题】:C++ difference between automatic type conversion to std::string and char*自动类型转换为 std::string 和 char* 之间的 C++ 区别
【发布时间】:2010-11-29 10:55:45
【问题描述】:

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

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

#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::stringchar* 在这种情况下的区别是否有什么特别之处,使得编译器不会将它们视为相同?

【问题讨论】:

标签: c++ type-conversion cstring stdstring


【解决方案1】:

有多种因素。如果你这样改变return语句

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获得想要的效果更耐人寻味...

【讨论】:

    【解决方案2】:

    在第一个示例中,两个比较的类(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== 一切正常。

    【讨论】:

    • 这个解释不正确。用 struct A { int a; 替换 std::string 的所有用法};,给A一个全局运算符==,它工作正常。问题不在于 std::string 是用户定义的类型,而是 basic_string 的模板 operator== (也就是说,string 没有自己的 operator==,只有一个从 basic_string 推导出来)。跨度>
    • 例如,另一个(愚蠢的)编译方法是定义bool operator==(const std::string &amp;lhs, const std::string &amp;rhs) {return lhs.compare(rhs) == 0;}。然后可以隐式执行到 std::string 的转换,以便使用此 operator== 获取两个字符串。
    【解决方案3】:

    问题是由于 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(Argument Dependent Lookup)这样的表达式时,一切正常。

    
    #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;   
    }
    

    【讨论】:

    • +1,当然,这正是发生的事情。标准中的段落是14.8.2.1,列出了参数推导过程中可能的转换。当然,不允许用户定义转换以使扣除成功。 14.8.1/4 如果参数不包含要推导的模板参数(不再),则最终允许进行所有隐式转换。
    • 14.8.3/1 的脚注包含进一步的解释(请注意,这只是提供信息,而不是规范):“推导参数允许的转换集是有限的,因为参数推导过程产生函数模板的参数要么完全匹配调用参数,要么仅在可以通过允许的有限转换桥接的方式上有所不同。非推导参数允许全范围的转换。"
    • @litb - 感谢您的参考:)
    • 感谢您的解释。但是,当我尝试您的代码时,我得到了一个段错误:调试器说“a == b”正在递归地调用自己。将其更改为“返回 std::operator==(a, b);”按预期工作。这会是正确的解决方法吗(如果我真的想这样做)?
    • @Paul Stephenson - 当然,是的!
    猜你喜欢
    • 2021-12-28
    • 1970-01-01
    • 2015-11-26
    • 2010-11-20
    • 2013-05-24
    • 2020-04-24
    • 1970-01-01
    • 2012-05-13
    • 2014-09-17
    相关资源
    最近更新 更多