Clang error: ambiguous conversion for static_cast - c++

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.

There are two ways for static_cast to convert an A into a B:
use A::operator B and call the implicit copy constructor B::B(const B& value) to create the 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 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 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?
Produce an error message
Make a logical choice by analogy to other rules
Contact the C++ standards committee and tell them to revise the standard.
?

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.

Related

Encounter the error: defaulted definition of default constructor is not constexpr when compile with clang-cl

I'm trying to compile these code with clang-cl with LLVM 15.
class Foo
{
public:
constexpr Foo() = default;
private:
int i;
};
int main(void)
{
Foo f;
}
The compilation command line is:
\> bazel build ... --compiler=clang-cl
The error will be displayed
cpp-constexpr/main.cpp(4,5): error: defaulted definition of default constructor is not constexpr
constexpr Foo() = default;
^
cpp-constexpr/main.cpp(13,9): error: no matching constructor for initialization of 'Foo'
Foo f;
^
cpp-constexpr/main.cpp(6,5): note: candidate constructor not viable: requires 1 argument, but 0 were provided
Foo(const Foo&) = delete;
^
2 errors generated.
Target //cpp-constexpr:constexpr failed to build
If it is compiled with MSVC 2022/2019, there will be no error at all. Looking forward to known your suggestions.
Thanks in advance.
TL;DR version: Compile to C++20 or a more recent version of the C++ Standard where both compilers should accept this code. Downside: you may find other discrepancies between their handling of C++20 and be no better off.
Explanation:
Quoting cppreference
for the constructor of a class or struct, every base class sub-object and every non-variant non-static data member must be initialized.
This is valid until C++20. As of C++20, the code should work as presented (but danged if I know what you'll do with a constant uninitialized variable i. Have to dig into the Standard or deeper into cppreference to see if i is zero initialized or something. This experiment with Matt Godbolt's compiler Explorer suggests it's not initialized.
So the fix is most likely to initialize i.
class Foo
{
public:
constexpr Foo() = default;
~Foo() = default;
Foo(const Foo&) = delete;
private:
int i = 0;
};
MSVC's ability to compile this code prior to C++20 appears to be a bug that could be fixed at any time. Rather than forcing incorrect behaviour onto clang-cl I recommend fixing the code (or compiling both sides to C++20 or more recent.

Is the local variable returned by a function automatically moved in C++20?

Please consider a C++ program, where the function foo builds its returning U object from a local variable of type int using one of two constructors:
struct U {
U(int) {}
U(int&&) {}
};
U foo(int a = 0) { return a; }
int main() { foo(); }
In C++17 mode the program is accepted by all compilers, demo: https://gcc.godbolt.org/z/b8hhEh948
However in C++20 mode, GCC rejects it with the error:
In function 'U foo(int)':
<source>:6:27: error: conversion from 'int' to 'U' is ambiguous
6 | U foo(int a = 0) { return a; }
| ^
<source>:3:5: note: candidate: 'U::U(int&&)'
3 | U(int&&) {}
| ^
<source>:2:5: note: candidate: 'U::U(int)'
2 | U(int) {}
| ^
demo: https://gcc.godbolt.org/z/fMvEPMGhq
I think this is because of C++20 feature P1825R0: Merged wording for P0527R1 and P1155R3 (more implicit moves)
And the function foo according to this feature must be equivalent to
U foo(int a = 0) { return std::move(a); }
which is rejected due to constructor selection ambiguity by all compilers, demo: https://gcc.godbolt.org/z/TjWeP965q
Is GCC the only right compiler in above example in C++20 mode?
What happens here is that the way that gcc went about implementing P1825.
In this example:
U foo(int a) {
return a;
}
The C++17 and C++20 language rules (no change here) are that we first treat a as an rvalue and if that overload resolution fails (in C++17, it was also required to bind to int&&), then we treat a as an lvalue. With that rule, this code works - the overload resolution with a as an xvalue fails due to ambiguity (because U is a silly type), so we fallback to treating a as an lvalue, and that succeeds.
But gcc's implementation doesn't do that. Instead it treats a as an xvalue that can also bind to non-const lvalue references, and performs a single round of overload resolution (it does so in an effort to avoid breaking some code, see here). That single round of overload resolution is ambiguous, because simply treating a as an xvalue is ambiguous and there's no lvalue ref constructor that is relevant here, so gcc rejects the example.
But in this case, I'd say this is U's fault rather than gcc's. If U either (a) had two constructors that took int const& and int&& as is usual or (b) had a single constructor that took an int, the example would compile fine. Note that in the (b) case, the C++17 rule would perform a copy but the C++20 rule would perform a move (since we no longer require that the constructor specifically takes an rvalue reference).

error: base class 'A1' has private copy constructor

Using Clang 3.7 on windows platform
See following code:
class A1
{
public:
A1(char* name){}
virtual ~A1() {}
private:
A1(const A1&) {}
};
class B1 : public A1
{
public:
B1(): A1(""){}
};
I get the following error:
MyFile(31): 8: error: base class 'A1' has private copy constructor
B1(): A1(""){}
^
MyFile(25): 2: note: declared private here
A1(const A1&) {}
^
Making A1 copy constructor public, eliminates the error!
What happened here?
Note: that by changing (as I should)
A1(const char* name)
I get no errors and all compile as expected
I imagine that this is just an artefact of how the diagnostics are generated.
First, lookup attempts to find a constructor to match your "call" (it's not a call but whatever)
The ctor taking char* doesn't match, as you know
The only other candidate is private
The compiler tries to see whether it can instantiate a temporary A1 from your "" argument to make that work
In doing so, again all it can find is private constructors
The compiler decides to complain about that before doing anything else
One might argue that this is a quality of implementation issue.
Amusingly, GCC 6.1.0 (even in pedantic C++14 mode) compiles your code as originally written, spitting out only a warning for the broken literal conversion.
You cannot call the constructor A1(char* name) using a string literal, becase a string literal is not convertible to char* (such deprecated conversion did exist prior to c++11). Or rather, a program that does call the constructor is ill-formed, and the implementation is allowed to refuse to compile.
As such, the overload resolution looks for other alternatives. The only other potential alternative that has the same number of arguments, is the copy constructor.
For whatever reason, clang appears to prefer the implicit conversion from string literal, to A1, thereby creating a temporary, that could be used for copy-initialization, over using the direct construction from the literal. This behaviour leads to the confusing compilation error.
Both alternatives are ill-formed, and clang appropriately warns about it: warning: ISO C++11 does not allow conversion from string literal to 'char *' [-Wwritable-strings]. The program does compile, if you set the standard mode to older than c++11 (in which case the program would be well-formed, even though it does use a deprecated conversion). Interestingly, if we disallow the conversion, then the program compiles even in the current standard mode:
class A1
{
public:
explicit A1(char* name){} // note the explicit
virtual ~A1() {}
private:
A1(const A1&) {}
};
G++ behaves differently and your program compiles fine (with the appropriate warning of course). Both compilers appear to comply to the standard in this regard.
Moral of the story: Always read the warnings as well. In this case, the warning was perfectly clear, and easy to solve, while the same bug indirectly caused an error that was not helpful in the solving of the bug.

Calling an explicit constructor with a braced-init list: ambiguous or not?

Consider the following:
struct A {
A(int, int) { }
};
struct B {
B(A ) { } // (1)
explicit B(int, int ) { } // (2)
};
int main() {
B paren({1, 2}); // (3)
B brace{1, 2}; // (4)
}
The construction of brace in (4) clearly and unambiguously calls (2). On clang, the construction of paren in (3) unambiguously calls (1) where as on gcc 5.2, it fails to compile with:
main.cpp: In function 'int main()':
main.cpp:11:19: error: call of overloaded 'B(<brace-enclosed initializer list>)' is ambiguous
B paren({1, 2});
^
main.cpp:6:5: note: candidate: B::B(A)
B(A ) { }
^
main.cpp:5:8: note: candidate: constexpr B::B(const B&)
struct B {
^
main.cpp:5:8: note: candidate: constexpr B::B(B&&)
Which compiler is right? I suspect clang is correct here, as the ambiguity in gcc can only arise through a path that involves implicitly constructing B{1,2} and passing that to the copy/move constructor - yet that constructor is marked explicit, so such implicit construction should not be allowed.
As far as I can tell, this is a clang bug.
Copy-list-initialization has a rather unintuitive behaviour: It considers explicit constructors as viable until overload resolution is completely finished, but can then reject the overload result if an explicit constructor is chosen. The wording in a post-N4567 draft, [over.match.list]p1
In copy-list-initialization, if an explicit constructor is chosen, the
initialization is ill-formed. [ Note: This differs from other
situations (13.3.1.3, 13.3.1.4), where only converting constructors
are considered for copy-initialization. This restriction only applies
if this initialization is part of the final result of overload
resolution. — end note ]
clang HEAD accepts the following program:
#include <iostream>
using namespace std;
struct String1 {
explicit String1(const char*) { cout << "String1\n"; }
};
struct String2 {
String2(const char*) { cout << "String2\n"; }
};
void f1(String1) { cout << "f1(String1)\n"; }
void f2(String2) { cout << "f2(String2)\n"; }
void f(String1) { cout << "f(String1)\n"; }
void f(String2) { cout << "f(String2)\n"; }
int main()
{
//f1( {"asdf"} );
f2( {"asdf"} );
f( {"asdf"} );
}
Which is, except for commenting out the call to f1, straight from Bjarne Stroustrup's N2532 - Uniform initialization, Chapter 4. Thanks to Johannes Schaub for showing me this paper on std-discussion.
The same chapter contains the following explanation:
The real advantage of explicit is that it renders f1("asdf") an
error. A problem is that overload resolution “prefers” non-explicit
constructors, so that f("asdf") calls f(String2). I consider the
resolution of f("asdf") less than ideal because the writer of
String2 probably didn’t mean to resolve ambiguities in favor of
String2 (at least not in every case where explicit and non-explicit
constructors occur like this) and the writer of String1 certainly
didn’t. The rule favors “sloppy programmers” who don’t use explicit.
For all I know, N2640 - Initializer Lists — Alternative Mechanism and Rationale is the last paper that includes rationale for this kind of overload resolution; it successor N2672 was voted into the C++11 draft.
From its chapter "The Meaning Of Explicit":
A first approach to make the example ill-formed is to require that all
constructors (explicit and non-explicit) are considered for implicit
conversions, but if an explicit constructor ends up being selected,
that program is ill-formed. This rule may introduce its own surprises;
for example:
struct Matrix {
explicit Matrix(int n, int n);
};
Matrix transpose(Matrix);
struct Pixel {
Pixel(int row, int col);
};
Pixel transpose(Pixel);
Pixel p = transpose({x, y}); // Error.
A second approach is to ignore the explicit constructors when looking
for the viability of an implicit conversion, but to include them when
actually selecting the converting constructor: If an explicit
constructor ends up being selected, the program is ill-formed. This
alternative approach allows the last (Pixel-vs-Matrix) example to work
as expected (transpose(Pixel) is selected), while making the
original example ("X x4 = { 10 };") ill-formed.
While this paper proposes to use the second approach, its wording seems to be flawed - in my interpretation of the wording, it doesn't produce the behaviour outlined in the rationale part of the paper. The wording is revised in N2672 to use the first approach, but I couldn't find any discussion about why this was changed.
There is of course slightly more wording involved in initializing a variable as in the OP, but considering the difference in behaviour between clang and gcc is the same for the first sample program in my answer, I think this covers the main points.
This is not a complete answer, even though it is too long as a comment.
I'll try to propose a counterexample to your reasoning and I'm ready to see downvote for I'm far from being sure.
Anyway, let's try!! :-)
It follows the reduced example:
struct A {
A(int, int) { }
};
struct B {
B(A) { }
explicit B(int, int ) { }
};
int main() {
B paren({1, 2});
}
In this case, the statement {1, 2} gives place apparently to two solutions:
direct initialization by means of B(A), because A(int, int) is not explicit and thus it is allowed and that's actually the first candidate
for the same reason above, it can be interpreted as B{B(A{1,2})} (well, let me abuse the notation to give you an idea and what I mean), that is {1,2} allows the construction of a B temporary object that is used immediately after as an argument for the copy/move constructor, and it's allowed again because the involved constructors are not explicit
The latter would explain the second and the third candidates.
Does it make sense?
I'm ready to delete the answers as long as you explain me what's wrong in my reasoning. :-)

C++11 vs C++98 conversion operator behavior changes?

I'm looking to use some c++11 features in some existing c++ projects, so I started changing compile flags in Clang for some projects, and I keep running into a specific issue regarding C++11's treatment of conversion operators (or cast operators) that I didn't expect to see and don't understand why this is now considered an error when it's been valid C++ code that's not c++11
I've boiled it down to this simple example:
#include <iostream>
#include <vector>
class SerializableFormat
{
public:
size_t i;
};
class A
{
public:
size_t x, y;
A(size_t n) : x(n), y(1) { }
operator const SerializableFormat() const
{
SerializableFormat result;
result.i = x;
if (y)
{
result.i /= y;
}
return result;
}
};
int main(int argc, const char * argv[])
{
std::vector<SerializableFormat> v;
for(size_t i = 0; i < 20; i++)
{
v.push_back(A(i));
}
return 0;
}
If Clang's compilation flags are set to -std=c++98 and libstdc++, there are no issues and this compiles fine.
If Clang's compilation flags are set to -std=c++11 and libc++, I get the error No viable conversion from 'A' to 'value_type' (aka 'SerializableFormat')
Just to make it clear-- in case you're thinking about giving SerializableFormat a constructor just for the class A:
Since the SerializableFormat class is more suited for conversion to and from various classes, it makes sense for A (and other classes that wish to be serializable) to have constructors and conversion operators rather than expect SerializableFormat to cover every type of class that wants to be serializable, so modifying SerializableFormat to have a special constructor is not a solution.
Can anyone see what I'm doing wrong here?
As the comments correctly note, you can get compiling by dropping the const in the return type of your SerializableFormat conversion operator:
operator const SerializableFormat() const
As to whether clang is correct in this behavior is a matter of some dispute. The issue is being tracked by clang bug report 16682. At this time there is talk of creating a CWG (C++ committee) issue report, but that has not yet been done. I note that this bug report has been open for some time now (2013-07-23), but was updated as recently as 2015-01-28.
In the meantime, practical advice is just never to return by const-value. This was decent advice for C++98/03, but with move semantics becomes bad advice because it will disable move semantics.