Just found the reason for an insidious crash to be a unchecked wild cast by the compiler, disregarding the types. Is this intended behaviour or a compiler bug?
Problem: When a type definition is involved, it is possible to make an implicit reinterpret cast, undermining the type system.
#include <iostream>
template<class A, class B>
inline bool
isSameObject (A const& a, B const& b)
{
return static_cast<const void*> (&a)
== static_cast<const void*> (&b);
}
class Wau
{
int i = -1;
};
class Miau
{
public:
uint u = 1;
};
int
main (int, char**)
{
Wau wau;
using ID = Miau &;
ID wuff = ID(wau); // <<---disaster
std::cout << "Miau=" << wuff.u
<< " ref to same object: " <<std::boolalpha<< isSameObject (wau, wuff)
<< std::endl;
return 0;
}
I was shocked to find out that gcc-4.9, gcc-6.3 and clang-3.8 accept this code without any error and produce the following output:
Miau=4294967295 ref to same object: true
Please note I use the type constructor syntax ID(wau). I would expect such behaviour on a C-style cast, i.e. (ID)wau. Only when using the new-style curly braces syntax ID{wau} we get the expected error...
~$ g++ -std=c++11 -o aua woot.cpp
woot.cpp: In function ‘int main(int, char**)’:
woot.cpp:31:21: error: no matching function for call to ‘Miau::Miau(<brace-enclosed initializer list>)’
ID wuff = ID{wau};
^
woot.cpp:10:7: note: candidate: constexpr Miau::Miau()
class Miau
^~~~
woot.cpp:10:7: note: candidate expects 0 arguments, 1 provided
woot.cpp:10:7: note: candidate: constexpr Miau::Miau(const Miau&)
woot.cpp:10:7: note: no known conversion for argument 1 from ‘Wau’ to ‘const Miau&’
woot.cpp:10:7: note: candidate: constexpr Miau::Miau(Miau&&)
woot.cpp:10:7: note: no known conversion for argument 1 from ‘Wau’ to ‘Miau&&’
Unfortunately, the curly-braces syntax is frequently a no-go in template heavy code, due to the std::initializer_list fiasco. So for me this is a serious concern, since the protection by the type system effectively breaks down here.
Can someone explain the reasoning behind this behaviour?
Is it some kind of backwards compatibility (again, sigh)?
To go full language-lawyer, T(expression) is a conversion of the result of expression to T1. This conversion has as effect to call the class' constructor2. This is why we tend to call a non-explicit constructor taking exactly one argument a conversion constructor.
using ID = Miau &;
ID wuff = ID(wau);
This is then equivalent to a cast expression to ID. Since ID is not a class type, a C-style cast occurs.
Can someone explain the reasoning behind this behaviour?
I really can't tell why is was ever part of C++. This is not needed. And it is harmful.
Is it some kind of backwards compatibility (again, sigh)?
Not necessarily, with C++11 to C++20 we've seen breaking changes. This could be removed some day, but I doubt it would.
1)
[expr.type.conv]
A simple-type-specifier or typename-specifier followed by a parenthesized optional expression-list or by a braced-init-list (the initializer) constructs a value of the specified type given the initializer. [...]
If the initializer is a parenthesized single expression, the type conversion expression is equivalent to the corresponding cast expression. [...]
2) (when T is of class type and such a constructor exists)
[class.ctor]/2
A constructor is used to initialize objects of its class type. Because constructors do not have names, they are never found during name lookup; however an explicit type conversion using the functional notation ([expr.type.conv]) will cause a constructor to be called to initialize an object.
it is possible to make an implicit reinterpret cast, undermining the type system.
ID wuff = ID(wau);
That's not an "implicit" reinterpret cast. That is an explicit type conversion. Although, the fact that the conversion does reinterpretation is indeed not easy to see. Specifically, the syntax of the cast is called "functional style".
If you're unsure what type of cast an explicit type conversion (whether using the functional syntax, or the C style syntax) performs, then you should refrain from using it. Many would argue that explicit type conversions should never be used.
If you had used static_cast instead, you would have stayed within the protection of the type system:
ID wuff = static_cast<ID>(wau);
error: non-const lvalue reference to type 'Miau' cannot bind to a value of unrelated type 'Wau'
It's often also safe to simply rely on implicit conversions:
ID wuff = wau;
error: non-const lvalue reference to type 'Miau' cannot bind to a value of unrelated type 'Wau'
Is this intended behaviour
Yes.
or a compiler bug?
No.
Related
I was making a thin derived class with a forwarding constructor. (Bear with me, I must use GCC 4.7.2, which lacks inherited constructors).
On the first try, I forgot to add the explicit keyword and got an error. Could someone explain exactly why this particular error occurs? I'm having trouble figuring out the sequence of events.
#include <memory>
template<typename T>
struct shared_ptr : std::shared_ptr<T>
{
template<typename...Args>
/*explicit*/ shared_ptr(Args &&... args)
: std::shared_ptr<T>(std::forward<Args>(args)...)
{}
};
struct A {};
struct ConvertsToPtr
{
shared_ptr<A> ptr = shared_ptr<A>(new A());
operator shared_ptr<A> const &() const { return ptr; }
};
int main()
{
shared_ptr<A> ptr;
ptr = ConvertsToPtr(); // error here
return 0;
}
The error:
test.cpp: In function ‘int main()’:
test.cpp:28:23: error: ambiguous overload for ‘operator=’ in ‘ptr = ConvertsToPtr()’
test.cpp:28:23: note: candidates are:
test.cpp:9:8: note: shared_ptr<A>& shared_ptr<A>::operator=(const shared_ptr<A>&)
test.cpp:9:8: note: shared_ptr<A>& shared_ptr<A>::operator=(shared_ptr<A>&&)
This is also the case with g++ 4.8.4 with the following:
g++ -g -pedantic --std=c++11 -o test main.cpp
The VS2015 settings are all defaulted.
The problem is that the compiler tries to convert a temporary returned by ConvertsToPtr() to a shared_ptr object. When the compiler is used with explicit keyword, then this conversion never occurs using the constructor. However, while examining with gdb it appears that instead it is using the shared_ptr<A> const &() conversion function to match the appropriate Type. This conversion then returns a const shared_ptr & which has no ambiguity when invoking the assignment operator (this is also match the findings of wojciech Frohmberg).
However, if the explicit is omitted, then an object of shared_ptr is returned. this can be matched either to rvalue version of the assignment operator or the const lvalue version.
According to N4296, Table-11, then we have, after the construction with the conversion constructor, a rvalue of shared_ptr object. However the overload resolution finds two matches, which both ranks under Exact Match (the rvalue version is Identity matching while the other is under Qualification matching).
I did check also on VS2015 and like stated in the comments, it works. But using some cout debugging one can see that the const lvalue assignment rvalue is prioritized over the rvalue const lvalue refrence version counterpart.
EDIT: I looked a little deeper in the standard and add the modification. the deleted text regarding the results VS2015 was wrong, because I didn't define both assignments. When both of assignments were declared it does prefer the rvalue.
I assume that the VS compiler distinct the Identity from the Qualification matching in ranking. However as I conclude, it is the VS compiler that is buggy. the g++ compilers obeys the given standard. However since GCC 5.0 Does work as Visual studio, The possibility of compiler bug is slim, so I would be happy to see another experts insights.
EDIT: In 13.3.3.2 one of the draw breakers, after the better ranking I wrote about it, is:
— S1 and S2 are reference bindings (8.5.3) and neither refers to an
implicit object parameter of a non-static member function declared
without a ref-qualifier, and S1 binds an rvalue reference to an rvalue
and S2 binds an lvalue reference.
There is an example attached showing that a given rvalue (not rvalue reference) is supposed to match a const int && over const int &. Therefore I guess, it is safe to assume that it is relevant to our case, even if we have && type and not const && type. I guess after all that GCC 4.7,4.8 is buggy after all.
You can find the text below in Appendix B of the book "C++ Templates The Complete Guide" by David Vandevoorde and Nicolai Josuttis.
B.2 Simplified Overload Resolution
Given this first principle, we are left with specifying how well a
given argument matches the corresponding parameter of a viable
candidate. As a first approximation we can rank the possible matches
as follows (from best to worst):
Perfect match. The parameter has the type of the expression, or it has a type that is a reference to the type of the expression (possibly
with added const and/or volatile qualifiers).
Match with minor adjustments. This includes, for example, the decay of an array variable to a pointer to its first element, or the
addition of const to match an argument of type int** to a parameter of
type int const* const*.
Match with promotion. Promotion is a kind of implicit conversion that includes the conversion of small integral types (such as bool,
char, short, and sometimes enumerations) to int, unsigned int, long or
unsigned long, and the conversion of float to double.
Match with standard conversions only. This includes any sort of standard conversion (such as int to float) but excludes the implicit
call to a conversion operator or a converting constructor.
Match with user-defined conversions. This allows any kind of implicit conversion.
Match with ellipsis. An ellipsis parameter can match almost any type (but non-POD class types result in undefined behavior).
A few pages later the book shows the following example and text (emphasis mine):
class BadString {
public:
BadString(char const*);
...
// character access through subscripting:
char& operator[] (size_t); // (1)
char const& operator[] (size_t) const;
// implicit conversion to null-terminated byte string:
operator char* (); // (2)
operator char const* ();
...
};
int main()
{
BadString str("correkt");
str[5] = 'c'; // possibly an overload resolution ambiguity!
}
At first, nothing seems ambiguous about the expression str[5]. The
subscript operator at (1) seems like a perfect match. However, it is
not quite perfect because the argument 5 has type int, and the
operator expects an unsigned integer type (size_t and std::size_t
usually have type unsigned int or unsigned long, but never type int).
Still, a simple standard integer conversion makes (1) easily viable.
However, there is another viable candidate: the built-in subscript
operator. Indeed, if we apply the implicit conversion operator to str
(which is the implicit member function argument), we obtain a pointer
type, and now the built-in subscript operator applies. This built-in
operator takes an argument of type ptrdiff_t, which on many platforms
is equivalent to int and therefore is a perfect match for the argument
5. So even though the built-in subscript operator is a poor match (by user-defined conversion) for the implied argument, it is a better
match than the operator defined at (1) for the actual subscript! Hence
the potential ambiguity.
Note that the first list is Simplified Overload Resolution.
To remove the "possibly" and "potential" about whether or not int is the same as ptrdiff_t, let's change one line:
str[(ptrdiff_t)5] = 'c'; // definitely an overload resolution ambiguity!
Now the message I get from g++ is:
warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second: [enabled by default]
and --pedantic-errors promotes that warning to an error.
So without diving into the standard, this tells you that the simplified list is only part of the story. That list tells you which of several possible routes to prefer when going from A to B, but it does not tell you whether to prefer a trip from A to B over a trip from C to D.
You can see the same phenomenon (and the same g++ message) more obviously here:
struct S {
explicit S(int) {}
operator int() { return 0; }
};
void foo(const S&, long) { }
void foo(int, int) { }
int main() {
S s(0);
foo(s, 1);
}
Again the call to foo is ambiguous, because when we have a choice of which argument to implicitly convert in order to choose an overload, the rules don't say, "pick the more lightweight conversion of the two and convert the argument requiring that conversion".
Now you're going to ask me for the standard citation, but I shall use the fact I need to hit the hay as an excuse for not looking it up ;-)
In summary it is not true here that "a user defined conversion is a better match than a standard integer conversion". However in this case the two possible overloads are defined by the standard to be equally good and hence the call is ambiguous.
str.operator[](size_t(5));
Write code in definite way and you'll not need to bother about all this stuff :)
There is much more to thing about.
Example:
#include <functional>
int main() {
auto test = []{};
test = []{};
return 0;
}
This emits the following error message in gcc 4.7.2:
test.cpp: In function ‘int main()’:
test.cpp:5:13: error: no match for ‘operator=’ in ‘test = <lambda closure object>main()::<lambda()>{}’
test.cpp:5:13: note: candidate is:
test.cpp:4:16: note: main()::<lambda()>& main()::<lambda()>::operator=(const main()::<lambda()>&) <deleted>
test.cpp:4:16: note: no known conversion for argument 1 from ‘main()::<lambda()>’ to ‘const main()::<lambda()>&’
From the standard 5.1.2.3 (emphasis mine):
An implementation may define the closure type differently from what is described below provided this does not alter the observable behavior of the program other than by changing:
— the size and/or alignment of the closure type,
— whether the closure type is trivially copyable (Clause 9)
— whether the closure type is a standard-layout class (Clause 9), or
— whether the closure type is a POD class (Clause 9).
As far as I can tell, this is what I'm running up against. It's attempting to use a deleted assignment operator and failing. I am curious to know if there's an easy workaround, and more broadly what the motivating rationale for allowing copy constructibility to be omitted for lambdas generally.
You seem to think that those two lambdas have the same type, but that is not true. Each one creates its own type:
#include <functional>
#include <type_traits>
#include <iostream>
int main() {
auto test = []{};
auto test2 = []{};
std::cout << std::is_same< decltype( test ), decltype( test2 ) >::value << std::endl;
return 0;
}
will print 0. Of course the error message you are getting from the compiler could be a little bit clearer in this regards...
The type of the
lambda-expression
(which is also the type of the closure object) is a unique, unnamed non-
union class type
So it is like you are doing the following:
struct {} a;
struct {} b;
a = b; // error, type mismatch
Use std::function if you want to assign different lambdas with the same signature to the same variable.
std::function<void()> f = []{};
f = []{}; //ok
Lambda can't be redefined because each lambda is of a different, anonymous, incompatible type.
They can be copied only if you pass them to a templated function (like std::function ctor) that would be able to deduce that type.
The reason you are not able to do this is because the copy assignment operator for the lambda-expression is declared deleted, See section 5.1.2/20 of the standard. For a more clear (for unusual definitions of clear) see this code sample
template<class T> void f(T x1)
{
T x2 = x1; // copy constructor exists, this operation will succeed.
x2 = x1; // assignment operator, deleted and will cause an error
}
int main()
{
f([]{});
return 0;
}
Other answers have pointed out that each lambda has a unique type, but this is not the reason why you are getting that error. This example shows that even if the two lambdas have the same type, it still is not able to copy it. However you are able to copy it to a new variable. This is the reason your error message is complaining about missing operator= and not about their types being different. Although each lambda having it's own type does not help you out much either.
If we could assign one lambda to another lambda of a different type, how do we copy the function bodies/definitions from that lambda to the other one? If we would be so stubborn, then we could use some member std::function-like type to be the one who will be copied. But that would be against the ol' C++ rule of not paying blah blah...
First, a tiny example that demonstrates the problem:
struct Bar {
enum Baz {aa, bb, cc};
Baz baz_;
operator Baz() const { return baz_; }
private:
template<typename T> operator T() const;
};
int main() {
Bar bar;
switch (bar) {
case Bar::aa:
break;
case Bar::bb:
break;
case Bar::cc:
break;
default:
break;
}
return 0;
}
Compiling this code with g++ 4.7.0 gives the following error:
foo.cpp: In function ‘int main()’:
foo.cpp:12:16: error: ambiguous default type conversion from ‘Bar’
foo.cpp:12:16: error: candidate conversions include ‘template<class T> Bar::operator T() const’
My understanding is that, since the struct object is being "switched" on, the compiler will try to find a conversion function to an integral or enum type. I explicitly provide a public conversion function to the Bar::Baz enum type and would like it to use that.
The confusing part to me is that the compiler also finds the private conversion function and then cannot decide which to use. Why is it even considering the private function? If I add an explicit cast, say switch((int)bar), then only the private conversion function matches and the compiler rightly complains that it cannot use it since it's private. So, since the private conversion function can't be used in this context, why isn't the choice between the two unambiguous?
Interestingly, I believe (though I'm not 100% sure) this code compiles without error on g++ 4.6.
edit: As pointed out by James McNellis in the comments, the fact that the private conversion function is templated is also relevant here.
Access control comes after overload resolution. This is specified in the standard, §13.3
Overload resolution is a mechanism for selecting the best function to call given a list of expressions that are
to be the arguments of the call and a set of candidate functions that can be called based on the context of the
call. The selection criteria for the best function are the number of arguments, how well the arguments
match the types of the parameters of the candidate function, how well (for nonstatic member functions) the
object matches the implied object parameter, and certain other properties of the candidate function. [Note:
the function selected by overload resolution is not guaranteed to be appropriate for the context. Other
restrictions, such as the accessibility of the function, can make its use in the calling context ill-formed. ]
So the overload resolution can chose a function that is not appropriate for the given context.
switch(expression)
The expression must be of an integral type or of a class type for which there is an unambiguous conversion to integral type.
I was merily experimenting with the new trailing return types, where I hit a problem with this (simplified) code
#include <list>
class MyContainer{
std::list<int> ints;
auto begin( ) -> decltype(ints.begin())
{
return ints.begin();
}
auto begin( ) const -> decltype(ints.begin())
{
return ints.begin();
}
};
Ignore the fact of how pointless this code is. The important part is the compiler error generated when using GCC 4.6.1 (with -std=c++0x flag):
In member function 'std::list<int>::iterator MyContainer::begin() const':
error: could not convert '((const MyContainer*)this)->MyContainer::ints.std::list<_Tp, _Alloc>::begin [with _Tp = int, _Alloc = std::allocator<int>, std::list<_Tp, _Alloc>::const_iterator = std::_List_const_iterator<int>]()' from 'std::list<int>::const_iterator {aka std::_List_const_iterator<int>}' to 'std::list<int>::iterator {aka std::_List_iterator<int>}'
In case you're not of fan of error involving templates, the short story is that in the body of the const version of MyContainer::begin, the expression ints.begin() returns a value of type std::list<int>::const_iterator (since ints is const in such a context). However, decltype(ints.begin()) produces the type std::list<int>::iterator, i.e. decltype ignores the const qualifier of the begin method when deciding the type of the expression. Unsurprisingly, a conflict in types is the result.
This seems to me to be a bug in the GCC compiler. It would only make sense for decltype to honor the const qualifier and produce the const_iterator type. Can anyone confirm or deny (maybe even explain) this? Maybe I am overlooking something in the mechanics of decltype, but this looks like a pretty straightforward scenario.
Note: as far as I can tell, the same behaviour holds not only for std::list<int>, but for any type with member functions overloaded on const-ness which return incompatible types.
You are correct, this is a bug. According to N3291, section 5.1.1, paragraph 3:
If a declaration declares a member function or member function template of a class X, the expression this is a prvalue of type “pointer to cv-qualifier-seq X” between the optional cv-qualifer-seq and the end of the function-definition, member-declarator, or declarator. It shall not appear before the optional cv-qualifier-seq and it shall not appear within the declaration of a static member function (although its type and value category are defined within a static member function as they are within a non-static member function). [Note: this is because declaration matching does not occur until the complete declarator is known. —end note ] Unlike the object expression in other contexts, *this is not required to be of complete type for purposes of class member access (5.2.5) outside the member function body. [Note: only class members declared prior to the declaration are visible. —end note ]
But this was a recent change between the last working draft and N3291. So GCC was right less than 6 months ago; that's the danger of writing code to a moving specification.