简体   繁体   中英

Clang error: ambiguous conversion for static_cast

I have the following piece of code:

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 reports the following error:

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.

I can't figure out why is the compiler generating an error although I have the conversion operator defined and I make the conversion at that specific place explicit using static_cast<>. The code passes compilation with GCC 4.5.2 and Visual Studio 2008 compilers. Clang version is 3.1, built by myself from the git repositories of Clang and LLVM a couple of days ago.

So, is Clang reporting an actual error? And if yes, why is it an error as it's not obvious to me at all(I won't ask why the other compilers are silent about that)?

UPDATE: the sample code is now a small compilable example(sorry for not doing this from the first time) and replicating the real situation I have. It seems that the conversion operator to AliasA is the problem, because if it's removed then everything compiles fine. The nasty thing right now is that for the above piece of code I get errors also from GCC.

UPDATE 2 : I added some code to the sample to reflect better my real situation; the only difference is that for the above sample I also get an error from GCC, whereas for my real code I don't.

I have filed a bug report about this to clang. They argue that it is not a bug. The C++ standard lacks a specification for this case and therefore the compiler reports it as ambiguous. See bug report .

I think this behavior is counter-intuitive. The first path through A::operator B() is a perfect match, while the second path involves three type conversions. The only logical thing to do is to regard the perfect match as superior.

What should a compiler do when a case is not explicitly resolved in the C++ standard?

  1. Produce an error message
  2. Make a logical choice by analogy to other rules
  3. Contact the C++ standards committee and tell them to revise the standard.

?

There are two ways for static_cast to convert an A into a B:

  1. use A::operator B and call the implicit copy constructor B::B(const B& value) to create the new B

  2. use A::operator AliasA , convert the result to AliasB and call B::B(const AliasB& value)

The compiler does not know which way to prefer. You can give a hint to use the second option by writing:

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

To use the first option you can omit the cast by simply writing:

    someFunc(counter, someA);

Well, I'm not sure if the latter is well definined behaviour, but it works at least with gcc and msvc.

I think I figured out in part what is happening, but I don't think I comprehend the whole situation. So the conversions paths the compiler sees look like below:

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

So it makes sense and the compiler is right in reporting the ambiguity (I love clan'g error and warning messages). One way to make it work is to break the conversion path via the AliasA(see the comments marked with (*) in the sample code in the question). It also works if I break the offending expression and rely only on implicit conversion, not trying to explicitly static_cast<> anything( see the comments marked with (**) in the sample code in the question). But I don't grasp completely what's happening behind the scenes. I still don't understand completely why it behaves different in the (**) case because the conversion paths seem to be the same, at least for the "theB.combine(high())" part.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM