【问题标题】:conversion operator overloading ambiguity, compilers differ转换运算符重载歧义,编译器不同
【发布时间】:2023-03-28 19:05:01
【问题描述】:

我在 SO 上看到了其他关于此的问题,但没有一个可以完整解释它。编译器处理以下两种情况的正确方法是什么?我已经用 gcc 4.7.1(使用 -std=c++0x)、VS2010 和 VS2012 进行了尝试,得到了不同的结果:

示例 1:

struct BB
{
 // generic cast
 template<typename T>
 operator T() const
 {   
   return 0;
 }

 // string cast
 operator std::string() const
 { 
   return string("hello");
 }
};

int main()
{
  BB b;
  string s = b;
}

输出:

  • gcc 4.7.1:好的
  • VS2010:好的
  • VS2012:失败:“无法从 BB 转换为 字符串”

示例 2:

struct BB
{
 // generic cast
 template<typename T>
 operator T() const
 {   
   return 0;
 }

 // string cast
 operator std::string() const
 { 
   return string("hello");
 }
};

int main()
{
  BB b;
  string s = (string)b;

输出:

  • gcc 4.7.1:失败:重载字符串(BB&)的调用不明确
  • VS2010:好的
  • VS2012:失败:“无法从 BB 转换为字符串”

【问题讨论】:

    标签: c++ visual-studio gcc operators


    【解决方案1】:

    您使用 C 风格演员表的第二个版本是模棱两可的。问题是static_cast&lt;std::string&gt; 可以通过两种方式将BB 类的实例转换为字符串。显而易见的路径是使用非模板 std::string 强制转换运算符直接转换为字符串。还有一条更曲折的路,除了VS2010,其他人都找到了。另一种方法是使用模板转换运算符将BB 转换为字符串的分配器,然后使用此分配器构造一个空字符串。

    模板转换运算符是潜在的危险野兽。您刚刚为编译器提供了将 BB 转换为 anything 的工具。

    您的第一个版本避开了这个问题,因为std::basic_string(const _Alloc&amp; __a) 是一个显式构造函数。显式构造函数可以被显式转换使用,但不能被隐式转换使用。正是这个构造函数加上对分配器的转换造成了歧义,并且这条路径不能与隐式转换一起使用。

    至于为什么 VS1012 在隐式转换上失败,可能是 VS2012 中的一个错误,也可能是 C++11 创造了更多从 BBstd::string 的途径。我不是 C++11 专家。我什至不是新手。

    还有一点:如果你用过,你的代码会失败得更惨

     std::string s;
     s = b;
    

    赋值运算符与模板转换运算符一起创建了更多从bs 的方法。系统应该将b 转换为std::stringchar 还是char*

    底线:您确实应该重新考虑使用模板转换运算符。

    【讨论】:

    • 我使用通用转换,因为我正在实现类似 boost::any 的东西,但也许有更好的方法。如果 mksvetkov 非常相似,会将其标记为答案
    【解决方案2】:

    在某些编译器下失败的原因是它们会竭尽全力试图弄清楚你在做什么。罪魁祸首是模板化的操作员调用。

    正如this SO question 解释的那样,必须显式调用模板化运算符(b.operator()&lt;std::string&gt;,如果你想调用template&lt;typename T&gt; operator();),但是,没有办法调用你的模板化运算符:

    b.operator std::string()<std::string>; //invalid
    b.operator std::string <std::string>(); //also invalid, string takes no template arguments
    // a bunch more weird syntax constructions...
    

    问题出在operator后面的名字依赖于模板参数,所以没办法指定。 gcc 和 VS2012 弄清楚了你在做什么,并注意到它们可以适应模板或明确定义的一个 => 模糊调用的转换。 VS2010没有这样做,正在调用其中一个,你可以通过调试找到哪一个。

    模板专业化在这种情况下可能会有所帮助,但是,尝试定义

    // string cast
    template<>
    operator std::string() const
    { 
        return string("hello");
    }
    

    将失败,因为编译器无法区分该函数与没有template&lt;&gt; 的常规函数​​之间的区别。如果原型中有一些参数它会起作用,但operator typename 确实有任何...

    长话短说 - 避免使用模板化的转换运算符。

    【讨论】:

    • 我仍然看不出与示例 1 和示例 2 的区别。在这两种情况下都必须进行字符串转换,对吧,因为字符串赋值运算符无法知道 BB?此外,似乎只有 gcc 对示例的处理方式不同
    • 在第一种情况下(b.operator std::string()&lt;std::string&gt;;),您尝试使用参数std::string 调用函数template &lt;typename T&gt; operator std::string();,在第二种情况下,您尝试调用operator std::string&lt;T&gt;(),但std ::string 不接受模板参数。
    • @mtsvetkov - 这不是造成差异的原因。不同之处在于显式转换构造函数禁止隐式转换。第一个示例是隐式转换,第二个示例是显式转换。
    猜你喜欢
    • 2021-12-20
    • 2023-04-09
    • 1970-01-01
    • 1970-01-01
    • 2021-05-02
    • 1970-01-01
    • 1970-01-01
    • 2014-10-04
    • 2013-05-14
    相关资源
    最近更新 更多