Clang错误:static_cast的转换不明确

Clang error: ambiguous conversion for static_cast

本文关键字:转换 不明确 cast 错误 static Clang      更新时间:2023-10-16

我有以下代码:

typedef int AliasB;
typedef unsigned short AliasA;
class Alias
{
public:
    explicit Alias(int someInt) { }
};
// (*) !! below breaks the conversion path via AliasA !!
//typedef Alias AliasA;
class C
{
public:
    C() { }
};
class B
{
public:
    B() { }
    B(const AliasB& value) { }
    operator AliasB() const
    {
        return -1000;
    }
    C combine(const B& someB) 
    {
        return C(); 
    }
};
class A
{
public:
    A() { }
    operator B() const
    {
         return B();
    }
    operator AliasA() const
    {
        return 1001;
        // (*) !! below breaks the conversion path via AliasA !!
        //return AliasA(1000);
    }
    A high() 
    {
        return A(); 
    }
    A low() 
    {
        return A(); 
    }
    C process()
    {
        return (static_cast<B>(low())).combine(static_cast<B>(high()));
        // (**) !! the below compiles fine !! 
        //B theB = low();
        //return theB.combine(high());
    }
};
inline int someFunc(unsigned int someParam, const B& bParam)
{
    return 1;
}
inline A createSomeA()
{
    return A();
}
int main ()
{
    A someA;
    unsigned int counter = 200;
    someFunc(counter, someA);
    //someFunc(counter, static_cast<B>(createSomeA()));
    someA.process();
    return 0;
}

Clang报告以下错误:

clang_static_test.cpp:66:17: error: ambiguous conversion for static_cast from 'A' to 'B'
        return (static_cast<B>(low())).combine(static_cast<B>(high()));
                ^~~~~~~~~~~~~~~~~~~~~
clang_static_test.cpp:21:7: note: candidate is the implicit copy constructor
class B
      ^
clang_static_test.cpp:25:5: note: candidate constructor
    B(const AliasB& value) { }
    ^
clang_static_test.cpp:66:48: error: ambiguous conversion for static_cast from 'A' to 'B'
        return (static_cast<B>(low())).combine(static_cast<B>(high()));
                                               ^~~~~~~~~~~~~~~~~~~~~~
clang_static_test.cpp:21:7: note: candidate is the implicit copy constructor
class B
      ^
clang_static_test.cpp:25:5: note: candidate constructor
    B(const AliasB& value) { }
    ^
2 errors generated.

尽管我有定义了转换运算符,我使用static_cast<>在特定位置显式进行转换。该代码通过了GCC 4.5.2和Visual Studio 2008编译器的编译。Clang版本是3.1,由我自己从Clang和LLVM的git存储库中构建几天前。

那么,Clang报告的是实际错误吗?如果是,为什么它是一个错误对我来说一点都不明显(我不会问为什么其他编译器对此保持沉默)?

UPDATE:示例代码现在是一个可编译的小示例(很抱歉第一次没有这么做),并复制了我的真实情况。似乎AliasA的转换运算符是个问题,因为如果删除了它,那么一切都会编译得很好。现在令人讨厌的是,对于上面的代码,我也从GCC中得到了错误。

UPDATE 2:我在示例中添加了一些代码,以更好地反映我的真实情况;唯一的区别是,对于上面的示例,我也从GCC中得到了一个错误,而对于我的真实代码,我没有。

static_cast有两种方法将A转换为B:

  1. 使用A::operator B并调用隐式复制构造函数B::B(const B& value)来创建新的B

  2. 使用A::operator AliasA,将结果转换为AliasB并调用B::B(const AliasB& value)

编译器不知道该选择哪种方式。您可以通过以下方式提示使用第二个选项:

    someFunc(counter, static_cast<AliasA>(someA));

要使用第一个选项,只需写以下内容即可省略强制转换:

    someFunc(counter, someA);

嗯,我不确定后者是否是定义良好的行为,但它至少适用于gcc和msvc。

我已经向clang提交了一份关于此问题的错误报告。他们认为这不是一个bug。C++标准缺乏针对这种情况的规范,因此编译器报告它不明确。请参阅错误报告。

我认为这种行为是违反直觉的。通过A::运算符B()的第一个路径是完全匹配的,而第二个路径涉及三个类型转换。唯一合乎逻辑的做法是把完美的匹配视为优越的。

当一个案例在C++标准中没有明确解决时,编译器应该怎么做?

  1. 生成错误消息
  2. 通过类比其他规则做出合乎逻辑的选择
  3. 联系C++标准委员会,让他们修改标准

我想我已经部分了解了发生了什么,但我不认为我理解整个情况。因此,编译器看到的转换路径如下所示:

              /--------  A  --------
              |                     |
              |                     |
              B             AliasA(unsigned short)
              |                     |
              |                     |
      copy ctor of B            AliasB(int)
                                    |
                                    |
                           ctor B(const AliasB& value)

所以这是有意义的,编译器报告歧义是正确的(我喜欢clan'g错误和警告消息)。使其工作的一种方法是通过AliasA中断转换路径(请参阅问题中示例代码中标记为(*)的注释)。如果我破坏了有问题的表达式,并且只依赖于隐式转换,而不尝试显式static_cast<>,它也会起作用任何内容(请参阅问题中示例代码中用(**)标记的注释)。但我并不完全了解幕后发生了什么。我仍然不完全理解为什么它在(**)的情况下表现不同,因为转换路径似乎是相同的,至少对于"B.combine(high())"部分。