【问题标题】:C++ implicit conversions in operands to standard operators操作数到标准运算符的 C++ 隐式转换
【发布时间】:2016-12-02 22:20:07
【问题描述】:

我在理解 C++ 使用隐式转换的条件时遇到了一些麻烦。假设我有一堂课:

class SomeClass
{
private:
    int val;
public:
    SomeClass(int i) :val{ i } {}
    operator string() { return to_string(val); }
};

为什么在将此类与运算符一起使用时,我需要强制转换为字符串?为什么它不只是隐式执行转换?

代码:

int main(void)
{
    SomeClass sc = 4;
    cout << (string)sc << endl; //prints 4, compiles fine
    cout << sc << endl;//does not compile, no operator "<<" matches these operands

    string str1 = sc;//compiles fine, performs cast
    string str2 = "Base String" + sc;//does not compile, no operator "+" matches these operands
}

这个问题比实际问题更具学术性,因为无论如何使用演员表更具可读性。

【问题讨论】:

    标签: c++ visual-c++ operators implicit-conversion


    【解决方案1】:

    在您编写cout &lt;&lt; (string)sc 时使用的operator&lt;&lt; overload 是一个函数模板:

    template <class CharT, class Traits, class Allocator>
    std::basic_ostream<CharT, Traits>&
        operator<<(std::basic_ostream<CharT, Traits>& os,
                   const std::basic_string<CharT, Traits, Allocator>& str);
    

    这是因为std::string 本身就是一个类模板,并且可以使用char 以外的其他类型进行实例化,并且如果将字符写入相同字符类型的流中,仍然应该可以打印。事实上,这正是std::wstring 所发生的情况,其中CharTwchar_t 而不是char。只要您使用合适的流,同样的operator&lt;&lt; 也有效。

    当您编写cout &lt;&lt; sc 时,会考虑特定的过载。然后,该重载被拒绝,因为编译器无法推断出要用于 CharT 和其他类型的类型。只有当它已经知道将执行转换时,它才能推断出类型,但现在还没有考虑转换,它们只会在稍后的阶段进行检查。

    如果有一个非模板operator&lt;&lt; 重载采用std::string,那么它将起作用,sc 将被隐式转换。

    【讨论】:

    • 那么,为了与 C++ 标准库函数(没有强制转换)更加兼容,我应该将隐式转换写入 basic_string 而不是字符串?
    • @ApoorvaKharche 不,stringbasic_string 的一个特定实例化的 typedef。 operator&lt;&lt; 是写成 basic_string&lt;char&gt; 还是写成 string 没有任何区别,因为它们的意思完全相同。 operator&lt;&lt; 是用 basic_string&lt;char&gt; 还是 basic_string&lt;T&gt; 来编写,其中要推导出 T 确实会有所不同,并且您无法从自己的类中控制它。
    【解决方案2】:

    std::cout 不接受 std::string
    它接受一个模板化的std::basic_string,其参数将被推导出来。
    详情请见here

    让我们考虑以下示例:

    #include<string>
    
    struct SomeClass {
        operator std::string() { return ""; }
    };
    
    template <class CharT, class Traits, class Allocator>
    void f(const std::basic_string<CharT, Traits, Allocator> &) {}
    
    template <class CharT, class Traits, class Allocator>
    void f(std::basic_string<CharT, Traits, Allocator> &) {}
    
    int main(void) {
        SomeClass sc{};
        f((std::string)sc);
        //f(sc);
    }
    

    这与您的情况类似。
    当然,f(sc) 的编译原因几乎相同。

    问题是要推导出模板参数SomeClass 应该转换为std::string。为了能够将其转换为std::string,编译器应该猜测存在有效的重载并尝试使用它。无论如何,从它的角度来看,这样一个可行的函数并不存在,因为模板参数仍然需要推导出来,编译器应该猜测在转换后存在SomeClass 的重载。它不能,因为……等等。

    我还没有检查过,但我敢打赌,operator+ 也有类似的情况。

    【讨论】:

    • 那么,为了与 C++ 标准库函数(没有强制转换)更加兼容,我应该将隐式转换写入 basic_string 而不是字符串?
    • 这个答案后不久,编辑历史看起来就像你基本上复制了我的,但编辑历史具有误导性。您发布了一个错误的答案,当您意识到这一点时将其删除,并最终修复它。最后,我们只是在同一时间发布了基本相同的答案。如果这恰好是有人投反对票的原因,请让那个人恢复投反对票。
    • @hvd 没错,我们同时发布了或多或少两个相同的答案,当然,我的答案是对之前发布的错误答案的编辑。 :-) ...无论如何,别担心,伤害我的不是反对票,而是没有解释的事实,如果我不知道读者有什么问题,我无法改进答案。
    猜你喜欢
    • 2010-10-27
    • 2013-08-25
    • 1970-01-01
    • 2010-12-09
    • 2012-05-04
    • 1970-01-01
    • 1970-01-01
    • 2014-08-18
    • 1970-01-01
    相关资源
    最近更新 更多