today I found that boost::hana's map and set aren't default constructable, while the tuple is. Is there any particular reason for this because it is quite annoying.
This
#include <boost/hana/set.hpp>
// ^^^ or map
constexpr boost::hana::set<> a{};
// ^^^ or map
int main(){}
fails with the following error:
main.cpp:3:30: error: no matching constructor for initialization of 'const boost::hana::set<>'
constexpr boost::hana::set<> a{};
^~~
/home/russellg/Documents/boost/hana-0.6.0/include/boost/hana/set.hpp:65:28: note: candidate constructor not viable: requires single argument 'xs', but no arguments were provided
explicit constexpr set(tuple<Xs...> const& xs)
^
/home/russellg/Documents/boost/hana-0.6.0/include/boost/hana/set.hpp:69:28: note: candidate constructor not viable: requires single argument 'xs', but no arguments were provided
explicit constexpr set(tuple<Xs...>&& xs)
^
/home/russellg/Documents/boost/hana-0.6.0/include/boost/hana/set.hpp:57:12: note: candidate constructor (the implicit copy constructor) not viable: requires 1 argument, but 0 were
provided
struct set
^
/home/russellg/Documents/boost/hana-0.6.0/include/boost/hana/set.hpp:57:12: note: candidate constructor (the implicit move constructor) not viable: requires 1 argument, but 0 were
provided
1 error generated.
even though it is perfectly valid to have an empty map or set:
#include <boost/hana/set.hpp>
// ^^^ or map
constexpr auto a = boost::hana::make_set();
// ^^^ or map
int main(){}
Which compiles flawlessly.
Any help is appreciated.
EDIT:
It doesn't actually matter if it is empty, it is always illegal to default construct maps and sets.
The hana::set and hana::map representations are implementation-defined. The documentation warns against their direct usage and mentions that the canonical way to create these is via hana::make_set and hana::make_map respectively. The documentation states:
The actual representation of a hana::set is implementation-defined. In particular, one should not take for granted the order of the template parameters and the presence of any constructor or assignment operator. The canonical way of creating a hana::set is through hana::make_set.
hana::tuple is a simpler container, documents its representation, and strives to maintain some parity with std::tuple. The hana::basic_tuple documentation notes:
[...] hana::tuple aims to provide an interface somewhat close to a std::tuple [...]
As to why hana::set and hana::map's representations are implementation-defined, consider reading the FAQ, but in short:
allows more flexibility to implement compile-time and runtime optimizations
knowing the type is usually not very useful
There is a github issue to consider adding a default-constructor for hana::map.
Related
Consider this example
#include <iostream>
struct A{
void* operator new(std::size_t N, std::align_val_t){ // #1
return malloc(sizeof(char)* N);
}
};
int main(){
auto ptr = new A; // #2
}
Both GCC and Clang complain that
<source>:9:17: error: no matching function for call to 'operator new'
auto ptr = new A;
^
<source>:4:11: note: candidate function not viable: requires 2 arguments, but 1 was provided
void* operator new(std::size_t N, std::align_val_t){
^
1 error generated.
However, [expr.new] p19 says
Overload resolution is performed on a function call created by assembling an argument list.
The first argument is the amount of space requested, and has type std::size_t.
If the type of the allocated object has new-extended alignment, the next argument is the type's alignment, and has type std::align_val_t.
If the new-placement syntax is used, the initializer-clauses in its expression-list are the succeeding arguments. If no matching function is found then
if the allocated object type has new-extended alignment, the alignment argument is removed from the argument list;
otherwise, an argument that is the type's alignment and has type std::align_val_t is added into the argument list immediately after the first argument;
and then overload resolution is performed again.
The found candidate for the allocation function calling in the new expression at #2 is #1. For the first time, the assembling argument list is sizeof(A), which cannot make #1 a matching function, then according to the rule, the assembling arguments will be sizeof(A),std::align_val_t(alignof(A)), which can make #1 a matching function. Also, it's a typical example recorded in [expr.new] p20
new T results in one of the following calls:
operator new(sizeof(T))
operator new(sizeof(T), std::align_val_t(alignof(T)))
Why do GCC and Clang reject this example? Is this defect of GCC and Clang? Or, Do I misunderstand something?
At present the only major compiler that implements CWG 2282 is MSVC. I'm not aware of any current effort or feature requests for GCC or clang.
Also, I don't believe the __cpp_aligned_new feature test macro has been updated for CWG 2282, so you'll need to use old-fashioned compiler version checking to determine whether the feature is available.
Help me solve this puzzle: In the following code I have an std::variant which forward declares a struct proxy which derives from this variant. This struct is only used because recursive using declarations are afaik not a thing in C++ (unfortunately). Anyway, I pull in all the base class constructors of the variant which define for each declared variant alternative T
template< class T >
constexpr variant( T&& t ) noexcept(/* see below */);
according to cppreference. I would assume that this means that a constructor for std::initializer_list<struct proxy> as type T is also defined. However, this doesn't seem to be the case. The following code results in an error:
#include <variant>
using val = std::variant<std::monostate, int, double, std::initializer_list<struct proxy>>;
struct proxy : val
{
using val::variant;
};
int main()
{
proxy some_obj = {1,2,3,2.5,{1,2}};
}
CompilerExplorer
Clang Error (because gcc doesn't go into much detail):
<source>:12:11: error: no matching constructor for initialization of 'proxy'
proxy some_obj = {1,2,3,2.5,{1,2}};
^ ~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/13.0.0/../../../../include/c++/13.0.0/variant:1434:2: note: candidate template ignored: could not match 'in_place_type_t<_Tp>' against 'int'
variant(in_place_type_t<_Tp>, initializer_list<_Up> __il,
^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/13.0.0/../../../../include/c++/13.0.0/variant:1455:2: note: candidate template ignored: could not match 'in_place_index_t<_Np>' against 'int'
variant(in_place_index_t<_Np>, initializer_list<_Up> __il,
^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/13.0.0/../../../../include/c++/13.0.0/variant:1424:2: note: candidate template ignored: could not match 'in_place_type_t<_Tp>' against 'int'
variant(in_place_type_t<_Tp>, _Args&&... __args)
^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/13.0.0/../../../../include/c++/13.0.0/variant:1444:2: note: candidate template ignored: could not match 'in_place_index_t<_Np>' against 'int'
variant(in_place_index_t<_Np>, _Args&&... __args)
^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/13.0.0/../../../../include/c++/13.0.0/variant:1401:7: note: candidate inherited constructor not viable: requires single argument '__rhs', but 5 arguments were provided
variant(const variant& __rhs) = default;
What I get from this is that the above mentioned constructor taking the variant alternatives T is not considered. Why?
Your proxy class does not have a declared constructor that accepts a std::initializer_list<proxy>. What it has, is a constructor template that accepts any type, T. But for that template to be chosen, the compiler has to deduce the type of T. The braced-init-list {1,2,3,2.5,{1,2}} does not have any inherent type though, so the compiler can't deduce the type T.
It's easy to think that a braced-init-list is a std::initializer_list, but that is not the case. There are some special cases where a std::initializer_list will be implicitly constructed from a braced-init-list, but deducing a template type parameter is not one of those cases.
You could explicitly construct a std::initializer_list<proxy>, i.e.
proxy some_obj = std::initializer_list<proxy>{1,2,3,2.5,std::initializer_list<proxy>{1,2}};
But keep in mind that std::initializer_list only holds pointers to its elements, and it does not extend their lifetimes. All of those proxy objects will go out of scope at the end of the full expression, and some_obj will immediately be holding a std::initializer_list full of dangling pointers. If you need a recursive type, you will almost certainly need to dynamically allocate the recursive children (and remember to clean them up, as well). std::initializer_list is not sufficient for this use case.
In following program, struct C has two constructors : one from std::initializer_list<A> and the other from std::initializer_list<B>. Then an object of the struct is created with C{{1}}:
#include <initializer_list>
struct A {
int i;
};
struct B {
constexpr explicit B(int) {}
};
struct C {
int v;
constexpr C(std::initializer_list<A>) : v(1) {}
constexpr C(std::initializer_list<B>) : v(2) {}
};
static_assert( C{{1}}.v == 1 );
Since only aggregate A can be implicitly constructed from int, one could expect that C(std::initializer_list<A>) is preferred and the program succeeds. And indeed it does in Clang.
However GCC complains:
error: call of overloaded 'C(<brace-enclosed initializer list>)' is ambiguous
note: candidate: 'constexpr C::C(std::initializer_list<B>)'
note: candidate: 'constexpr C::C(std::initializer_list<A>)'
and so does MSVC:
error C2440: '<function-style-cast>': cannot convert from 'initializer list' to 'C'
note: No constructor could take the source type, or constructor overload resolution was ambiguous
Demo: https://gcc.godbolt.org/z/joz91q4ed
Which compiler is correct here?
The wording could be clearer (which is unsurprising), but GCC and MSVC are correct here: the relevant rule ([over.ics.list]/7) checks only that
overload resolution […] chooses a single best constructor […] to perform the initialization of an object of type X from the argument initializer list
so the fact that the initialization of B from {1} would be ill-formed is irrelevant.
There are several such places where implicit conversion sequences are more liberal than actual initialization, causing certain cases to be ambiguous even though some of the possibilities wouldn’t actually work. If the programmer was confused and thought one of those near misses was actually a better match, it’s a feature that the ambiguity is reported.
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.
I came across a rather strange case of overload resolution today. I reduced it to the following:
struct S
{
S(int, int = 0);
};
class C
{
public:
template <typename... Args>
C(S, Args... args);
C(const C&) = delete;
};
int main()
{
C c({1, 2});
}
I fully expected C c({1, 2}) to match the first constructor of C, with the number of variadic arguments being zero, and {1, 2} being treated as an initializer list construction of an S object.
However, I get a compiler error that indicates that instead, it matches the deleted copy constructor of C!
test.cpp: In function 'int main()':
test.cpp:17:15: error: use of deleted function 'C(const C &)'
test.cpp:12:5: error: declared here
I can sort of see how that might work - {1, 2} can be construed as a valid initializer for C, with the 1 being an initializer for the S (which is implicitly constructible from an int because the second argument of its constructor has a default value), and the 2 being a variadic argument... but I don't see why that would be a better match, especially seeing as the copy constructor in question is deleted.
Could someone please explain the overload resolution rules that are in play here, and say whether there is a workaround that does not involve mentioning the name of S in the constructor call?
EDIT: Since someone mentioned the snippet compiles with a different compiler, I should clarify that I got the above error with GCC 4.6.1.
EDIT 2: I simplified the snippet even further to get an even more disturbing failure:
struct S
{
S(int, int = 0);
};
struct C
{
C(S);
};
int main()
{
C c({1});
}
Errors:
test.cpp: In function 'int main()':
test.cpp:13:12: error: call of overloaded 'C(<brace-enclosed initializer list>)' is ambiguous
test.cpp:13:12: note: candidates are:
test.cpp:8:5: note: C::C(S)
test.cpp:6:8: note: constexpr C::C(const C&)
test.cpp:6:8: note: constexpr C::C(C&&)
And this time, GCC 4.5.1 gives the same error, too (minus the constexprs and the move constructor which it does not generate implicitly).
I find it very hard to believe that this is what the language designers intended...
For C c({1, 2}); you have two constructors that can be used. So overload resolution takes place and looks what function to take
C(S, Args...)
C(const C&)
Args will have been deduced to zero as you figured out. So the compiler compares constructing S against constructing a C temporary out of {1, 2}. Constructing S from {1, 2} is straight forward and takes your declared constructor of S. Constructing C from {1, 2} also is straight forward and takes your constructor template (the copy constructor is not viable because it has only one parameter, but two arguments - 1 and 2 - are passed). These two conversion sequences are not comparable. So the two constructors would be ambiguous, if it weren't for the fact that the first is a template. So GCC will prefer the non-template, selecting the deleted copy constructor and will give you a diagnostic.
Now for your C c({1}); testcase, three constructors can be used
C(S)
C(C const&)
C(C &&)
For the two last, the compiler will prefer the third because it binds an rvalue to an rvalue. But if you consider C(S) against C(C&&) you won't find a winner between the two parameter types because for C(S) you can construct an S from a {1} and for C(C&&) you can initialize a C temporary from a {1} by taking the C(S) constructor (the Standard explicitly forbids user defined conversions for a parameter of a move or copy constructor to be usable for an initialization of a class C object from {...}, since this could result in unwanted ambiguities; this is why the conversion of 1 to C&& is not considered here but only the conversion from 1 to S). But this time, as opposed to your first testcase, neither constructor is a template so you end up with an ambiguity.
This is entirely how things are intended to work. Initialization in C++ is weird so getting everything "intuitive" to everyone is going to be impossible sadly. Even a simple example as above quickly gets complicated. When I wrote this answer and after an hour I looked at it again by accident I noticed I overlooked something and had to fix the answer.
You might be correct in your interpretation of why it can create a C from that initializer list. ideone happily compiles your example code, and both compilers can't be correct. Assuming creating the copy is valid, however...
So from the compiler's point of view, it has two choices: Create a new S{1,2} and use the templated constructor, or create a new C{1,2} and use the copy constructor. As a rule, non-template functions are preferred over template ones, so the copy constructor is chosen. Then it looks at whether or not the function can be called... it can't, so it spits out an error.
SFINAE requires a different type of error... they occur during the first step, when checking to see which functions are possible matches. If simply creating the function results in an error, that error is ignored, and the function not considered as a possible overload. After the possible overloads are enumerated, this error suppression is turned off and you're stuck with what you get.