Clang error: ambiguous conversion for static_cast

I have the following code snippet:

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 understand why the compiler generates an error, although I have a conversion operator, and I make the conversion in this particular place explicit using static_cast <>. The code passes the compilation with the GCC 4.5.2 and Visual Studio 2008 compilers. The Clang version is 3.1, which I built from the git Clang and LLVM repositories a couple of days ago.

So, is Clang reporting an actual error? And if so, why is this a mistake, because (I will not ask why other compilers are silent about this)?

UPDATE: The sample code is now a small compiled example (sorry for not doing it the first time) and replicating the real situation that I have. It seems that the conversion operator in AliasA is a problem, because if it is deleted, everything compiles fine. Now the unpleasant thing is that for the above code snippet, I get errors also from GCC.

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

+6
source share
3 answers

I think I’ve partially figured out what’s going on, but I don’t think I understand the whole situation. Thus, the conversion paths that the compiler sees are as follows:

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

So that makes sense, and the compiler is right, reporting ambiguity (I love clan'g messages and warnings). One way to make it work is to break the conversion path through AliasA (see Comments marked with (*) in the sample code in the question). It also works if I break the violating expression and rely only on the implicit conversion, not trying to explicitly specify static_cast <> anything (see Comments marked with (**) in the code example in the question). But I don’t understand what is going on behind the scenes. I still do not completely understand why it behaves differently in the case of (**), because the conversion paths seem to be the same, at least for the "theB.combine (high ()) part.

0
source

There are two ways to convert static_cast A to B:

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

  • 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 throw by simply writing:

  someFunc(counter, someA); 

Well, I'm not sure that the latter is well defined, but works with at least gcc and msvc.

+3
source

I wrote an error message for this. They claim that this is not a mistake. The C ++ standard does not have a specification for this case, so the compiler reports this as ambiguous. See .

I think this behavior is contrary to intuition. The first path through A :: operator B () is a perfect match, and the second path includes three types of transformations. The only thing to do is to consider the perfect match excellent.

What should the compiler do if the case is not explicitly permitted in the C ++ standard?

  • Display error message
  • Make a logical choice, similar to other rules.
  • Contact the C ++ Standards Committee and ask them to revise the standard.

?

+3
source

Source: https://habr.com/ru/post/911856/


All Articles