incomplete type support for list - c++11

I implemented a list with similar API to std::list but it fails to compile
struct A { my_list<A> v; };
The list has a base class that has a member a base_node which has the prev and next fields and node (which is derived from base_node) holds the T value (which is the template parameter for the list). The compilation error is
error: ‘node<T>::val’ has incomplete type
T val;
^~~
note: forward declaration of ‘struct A’
I looked in GCC code and it seems like they hold a buffer of bytes of size T so not sure how it works for them. How std::list manages to store A in its nodes?
[UPDATE]
struct A { };
template <typename T>
struct B : public A
{
using B_T = B<T>;
T t;
};
template <typename T>
class C
{
using B_T = typename B<T>::B_T; // this fails to compile
//using B_T = B<T>; // this compiles fine
};
struct D { C<D> d; };

In your simplified example
struct A { };
template <typename T>
struct B : public A
{
using B_T = B<T>;
T t;
};
template <typename T>
class C
{
using B_T = typename B<T>::B_T; // this fails to compile
//using B_T = B<T>; // this compiles fine
};
struct D { C<D> d; };
you're running into the gotchas of class template instantiation.
First, note that a class definition has essentially two parse passes (not necessarily implemented this way):
First determine the types of base classes and class members. During this process, the class is considered incomplete, although previously declared bases and members can be used by later code in the definition.
In some pieces of code within the class definition which does not affect the types of bases or members, the class is considered complete. These places include member function definition bodies, member function default arguments, static member initializers, and non-static member default initializers.
For example:
struct S {
std::size_t n = sizeof(S); // OK, S is complete
std::size_t f() const { return sizeof(S); } // OK, S is complete
using array_type = int[sizeof(S)]; // Error, S incomplete
void f(int (&)[sizeof(S)]); // Error, S incomplete
};
Templates make this trickier because they make it easier to accidentally indirectly use a class which is not yet complete. This particularly comes up in CRTP code, but this example is another simple way it can happen.
The basic way class template instantiation works (a bit simplified) is:
Just naming a class template specialization, like X<Y>, does not by itself cause the class template to be instantiated.
Using a class template specialization in ways valid for an incomplete class type does not cause the template to be instantiated.
Using a class template specialization in any way which requires the type to be complete, like naming a member of the class or defining a variable with the class type (not pointer or reference), causes an implicit instantiation of the template.
Instantiating a class template involves determining the types of the base classes and members, much like the "first pass" of class definition parsing. All those base and member types must be valid at that time. Instantiating member definitions is for the most part delayed until each member is needed, but there is no selective instantiation of member types in this step: it's all or error.
The process can be recursive when a base class or member declaration involves another template specialization. But during that other instantiation, the class type for the original instantiation context is considered incomplete.
Looking at the example, struct D defines a member C<D> d; which requires C<D> to be complete, so we attempt to instantiate the specialization C<D>. So far, D is incomplete.
There's just one member of C<D>, which is
using B_T = typename B<D>::B_T;
Since this names a member of another class template specialization B<D>, now we have to attempt to instantiate that B<D> specialization. So far, D and C<D> are still incomplete.
B<D> has one base class, which is just A. It has two members:
using B_T = B<D>;
D t;
The member type B<D>::B_T is fine since just naming B<D> doesn't require a complete type. But instantiating B<D> requires both members to be well-formed. A class member can't have an incomplete class as its type, but type D is still incomplete right now.
As you noticed, you can work around this by avoiding naming the member B<T>::B_T and directly using the type B<T> instead. Or you could move the original B_T definition to some other base class or traits struct, and make sure its new location is one that can be instantiated with an incomplete type as argument.
Many templates just assume their arguments must always be complete types. But they can be useful in more situations if they're carefully written with considerations about how the code uses template arguments and other indirectly used dependent types, which might be incomplete at the point of instantiation.

Related

Remove class member type part from decltype

I ran into I case I had not seen before, while using decltype on a member of a templated class. I wanted to make a nicer make_unique so that changing type on the member does not cause fixing the make_unique calls. I wanted to avoid this using decltype(member)::element_type as the type for make_unique but got an error. Here is a simple snippet that shows the error (and I understand why it is shown):
#include <memory>
template<typename T>
struct foo
{
foo()
{
// g++ gives:
// dependent-name 'decltype (((foo<T>*)this)->foo<T>::p_)::element_type' is parsed as a non-type, but instantiation yields a type
// say 'typename decltype (((foo<T>*)this)->foo<T>::p_)::element_type' if a type is meant
//
// How can I atleast remove the class name from the type?
p_ = std::make_unique<decltype(p_)::element_type>();
// g++ gives:
// dependent-name 'decltype (p)::element_type' is parsed as a non-type, but instantiation yields a type
// say 'typename decltype (p)::element_type' if a type is meant
//
// makes sense since p here is dependent on T
std::unique_ptr<T> p = std::make_unique<decltype(p)::element_type>();
// This one is fine, makes sense, since the type is known
std::unique_ptr<int> p2 = std::make_unique<decltype(p2)::element_type>();
}
std::unique_ptr<T> p_;
};
int main()
{
foo<int> f;
return 0;
}
My question is, is there a nice/pretty way to remove the 'is a member of' ((foo<T>*)this)->foo<T>::p_))part from the decltype value, so that at least I could use the same fix and simply provide typename on the member variable p_ ? The long fix suggested by g++ seems kind of ugly.
5 minutes after posting I had an idea that I could do
p_ = std::make_unique<decltype(std::remove_reference(*p_)::type)>();
but that seems to give a parse error.
You can simply place a typename before decltype().
I mean
p_ = std::make_unique<typename decltype(p_)::element_type>();

Specialized template accepting constructor parameter when only default constructor defined

So, I have this template class and its specialization.
#include <iostream>
using namespace std;
template<bool> struct CompileTimeChecker{
CompileTimeChecker(...); //constructor, can accept any number of parameters;
};
//specialized template definition
template<> struct CompileTimeChecker<false> {
//default constructor, body empty
};
Case 1:
In the main function I am defining a local class called ErrorA. When I create a temporary of CompileTimeChecker<false> with temporary object of ErrorA fed as an initializer, the compiler is not detecting any error.
int main()
{
class ErrorA {};
CompileTimeChecker<false>(ErrorA()); //Case 1;
CompileTimeChecker<false>(int()); //Case 2;
return 0;
}
Case 2:
Next I feed it with temporary object of type int, and suddenly the compiler recognizes the issue (there is no constructor that takes args in the specialized template CompileTimeChecker<false>)
main.cpp:30:36: error: no matching function for call to ‘CompileTimeChecker::CompileTimeChecker(int)’ CompileTimeChecker<false>(int());
main.cpp:21:23: note: candidate: constexpr CompileTimeChecker::CompileTimeChecker()
template<> struct CompileTimeChecker<false> {
^~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:21:23: note: candidate expects 0 arguments, 1 provided
Why does it not recognize the issue in case 1?
CompileTimeChecker<false>(ErrorA());
does not create a temporary of type CompileTimeChecker<false>, passing a temporary ErrorA() to its constructor. Rather, it declares a function named ErrorA, taking no parameters and returning CompileTimeChecker<false> . See also: most vexing parse.
On the other hand, CompileTimeChecker<false>(int()); cannot be parsed as a declaration, so it does unambiguously create a temporary of type CompileTimeChecker<false>.
The easiest way out is to use braces in place of parens to indicate initialization:
CompileTimeChecker<false>{ErrorA{}};

std::map of non-movable objects [duplicate]

The following code will not compile on gcc 4.8.2.
The problem is that this code will attempt to copy construct an std::pair<int, A> which can't happen due to struct A missing copy and move constructors.
Is gcc failing here or am I missing something?
#include <map>
struct A
{
int bla;
A(int blub):bla(blub){}
A(A&&) = delete;
A(const A&) = delete;
A& operator=(A&&) = delete;
A& operator=(const A&) = delete;
};
int main()
{
std::map<int, A> map;
map.emplace(1, 2); // doesn't work
map.emplace(std::piecewise_construct,
std::forward_as_tuple(1),
std::forward_as_tuple(2)
); // works like a charm
return 0;
}
As far as I can tell, the issue isn't caused by map::emplace, but by pair's constructors:
#include <map>
struct A
{
A(int) {}
A(A&&) = delete;
A(A const&) = delete;
};
int main()
{
std::pair<int, A> x(1, 4); // error
}
This code example doesn't compile, neither with coliru's g++4.8.1 nor with clang++3.5, which are both using libstdc++, as far as I can tell.
The issue is rooted in the fact that although we can construct
A t(4);
that is, std::is_constructible<A, int>::value == true, we cannot implicitly convert an int to an A [conv]/3
An expression e can be implicitly converted to a type T if and only if the declaration T t=e; is well-formed,
for some invented temporary variable t.
Note the copy-initialization (the =). This creates a temporary A and initializes t from this temporary, [dcl.init]/17. This initialization from a temporary tries to call the deleted move ctor of A, which makes the conversion ill-formed.
As we cannot convert from an int to an A, the constructor of pair that one would expect to be called is rejected by SFINAE. This behaviour is surprising, N4387 - Improving pair and tuple analyses and tries to improve the situation, by making the constructor explicit instead of rejecting it. N4387 has been voted into C++1z at the Lenexa meeting.
The following describes the C++11 rules.
The constructor I had expected to be called is described in [pairs.pair]/7-9
template<class U, class V> constexpr pair(U&& x, V&& y);
7 Requires: is_constructible<first_type, U&&>::value is true and
is_constructible<second_type, V&&>::value is true.
8 Effects: The
constructor initializes first with std::forward<U>(x) and second with
std::forward<V>(y).
9 Remarks: If U is not implicitly convertible to
first_type or V is not implicitly convertible to second_type this
constructor shall not participate in overload resolution.
Note the difference between is_constructible in the Requires section, and "is not implicitly convertible" in the Remarks section. The requirements are fulfilled to call this constructor, but it may not participate in overload resolution (= has to be rejected via SFINAE).
Therefore, overload resolution needs to select a "worse match", namely one whose second parameter is a A const&. A temporary is created from the int argument and bound to this reference, and the reference is used to initialize the pair data member (.second). The initialization tries to call the deleted copy ctor of A, and the construction of the pair is ill-formed.
libstdc++ has (as an extension) some nonstandard ctors. In the latest doxygen (and in 4.8.2), the constructor of pair that I had expected to be called (being surprised by the rules required by the Standard) is:
template<class _U1, class _U2,
class = typename enable_if<__and_<is_convertible<_U1, _T1>,
is_convertible<_U2, _T2>
>::value
>::type>
constexpr pair(_U1&& __x, _U2&& __y)
: first(std::forward<_U1>(__x)), second(std::forward<_U2>(__y)) { }
and the one that is actually called is the non-standard:
// DR 811.
template<class _U1,
class = typename enable_if<is_convertible<_U1, _T1>::value>::type>
constexpr pair(_U1&& __x, const _T2& __y)
: first(std::forward<_U1>(__x)), second(__y) { }
The program is ill-formed according to the Standard, it is not merely rejected by this non-standard ctor.
As a final remark, here's the specification of is_constructible and is_convertible.
is_constructible [meta.rel]/4
Given the following function prototype:
template <class T>
typename add_rvalue_reference<T>::type create();
the predicate condition for a template specialization is_constructible<T, Args...> shall be satisfied if and only if the following variable definition would be well-formed for some invented variable t:
T t(create<Args>()...);
[Note: These tokens are never interpreted as a function declaration. — end note] Access checking is performed as if in a context unrelated to T and any of the Args. Only the validity of the immediate context of the variable initialization is considered.
is_convertible [meta.unary.prop]/6:
Given the following function prototype:
template <class T>
typename add_rvalue_reference<T>::type create();
the predicate condition for a template specialization is_convertible<From, To> shall be satisfied if and
only if the return expression in the following code would be well-formed, including any implicit conversions
to the return type of the function:
To test() {
return create<From>();
}
[Note: This requirement gives well defined results for reference types, void types, array types, and function types. — end note] Access checking is performed as if in a context unrelated to To and From. Only
the validity of the immediate context of the expression of the return-statement (including conversions to
the return type) is considered.
For your type A,
A t(create<int>());
is well-formed; however
A test() {
return create<int>();
}
creates a temporary of type A and tries to move that into the return-value (copy-initialization). That selects the deleted ctor A(A&&) and is therefore ill-formed.

Preferred way of class member initialization?

class A { public: int x[100]; };
Declaring A a will not initialize the object (to be seen by garbage values in the field x).
The following will trigger initialization: A a{} or auto a = A() or auto a = A{}.
Should any particular one of the three be preferred?
Next, let us make it a member of another class:
class B { public: A a; };
The default constructor of B appears to take care of initialization of a.
However, if using a custom constructor, I have to take care of it.
The following two options work:
class B { public: A a; B() : a() { } };
or:
class B { public: A a{}; B() { } };
Should any particular one of the two be preferred?
Initialization
class A { public: int x[100]; };
Declaring A a will not initialize the object (to be seen by garbage
values in the field x).
Correct A a is defined without an initializer and does not fulfill any of the requirements for default initialization.
1) The following will trigger initialization:
A a{};
Yes;
a{} performs list initialization which
becomes value initialization if {} is empty, or could be aggregate initialization if A is an aggregate.
Works even if the default constructor is deleted. e.g. A() = delete; (If 'A' is still considered an aggregate)
Will warn of narrowing conversion.
2) The following will trigger initialization:
auto a = A();
Yes;
This is copy initialization where a prvalue temporary is constructed with direct initialization () which
uses value initialization if the () is empty.
No hope of aggregate initialization.
The prvalue temporary is then used to direct-initialize the object.
Copy elision may be, and normally is employed, to optimize out the copy and construct A in place.
Side effects of skipping copy/move constructors are allowed.
Move constructor may not be deleted. e.g A(A&&) = delete;
If copy constructor is deleted then move constructor must be present. e.g. A(const A&) = delete; A(A&&) = default;
Will not warn of narrowing conversion.
3) The following will trigger initialization:
auto a = A{}
Yes;
This is copy initialization where a prvalue temporary is constructed with list initialization {} which
uses value initialization if {} is empty, or could be aggregate initialization if A is an aggregate.
The prvalue temporary is then used to direct-initialize the object.
Copy elision may be, and normally is employed, to optimize out the copy and construct A in place.
Side effects of skipping copy/move constructors are allowed.
Move constructor may not be deleted. e.g A(A&&) = delete;
If copy constructor is deleted then move constructor must be present. e.g. A(const A&) = delete; A(A&&) = default;
Will warn of narrowing conversion.
Works even if the default constructor is deleted. e.g. A() = delete; (If 'A' is still considered an aggregate)
Should any particular one of the three be preferred?
Clearly you should prefer A a{}.
Member Initialization
Next, let us make it a member of another class:
class B { public: A a; };
The default constructor of B appears to take care of initialization
of a.
No this is not correct.
the implicitly-defined default constructor of 'B' will call the default constructor of A, but will not initialize the members. No direct or list initialization will be triggered. Statement B b; for this example will call the default constructor, but leaves indeterminate values of A's array.
1) However, if using a custom constructor, I have to take care of it. The
following two options work:
class B { public: A a; B() : a() { } };
This will work;
: a() is a constructor initializer and a() is a member initializer as part of the member initializer list.
Uses direct initialization () or, if () is empty, value initialization.
No hope of using aggregate initialization.
Will not warn of narrowing conversion.
2) or:
class B { public: A a{}; B() { } };
This will work;
a now has a non-static data member initializer, which may require a constructor to initialize it if you are using aggregate initialization and the compiler is not fully C++14 compliant.
The member initializer uses list initialization {} which
may become either value initialization if {} is empty or aggregate initialization if A is an aggregate.
If a is the only member then the default constructor does not have to be defined and the default constructor will be implicitly defined.
Clearly you should prefer the second option.
Personally, I prefer using braces everywhere, with some exceptions for auto and cases where a constructor could mistake it for std::initializer_list:
class B { public: A a{}; };
A std::vector constructor will behave differently for std::vector<int> v1(5,10) and std::vector<int> v1{5,10}. with (5,10) you get 5 elements with the value 10 in each one, but with {5,10} you get two elements containing 5 and 10 respectively because std::initializer_list is strongly preferred if you use braces. This is explained very nicely in item 7 of Effective Modern C++ by Scott Meyers.
Specifically for member initializer lists, two formats may be considered:
Direct initialization a() which becomes value initialization if the () is empty.
List initialization a{} which also becomes value initialization if {} is empty.
In member initializer lists, fortunately, there is no risk of the most vexing parse. Outside of the initializer list, as a statement on its own, A a() would have declared a function vs. A a{} which would have been clear. Also, list initialization has the benefit of preventing narrowing conversions.
So, in summary the answer to this question is that it depends on what you want to be sure of and that will determine the form you select. For empty initializers the rules are more forgiving.

A function with a std::function parameter does not accept a lamba function

I am trying to get more familiar with the C++11 standard by implementing the std::iterator on my own doubly linked list collection and also trying to make my own sort function to sort it.
I would like the sort function to accept a lamba as a way of sorting by making the sort accept a std::function, but it does not compile (I do not know how to implement the move_iterator, hence returning a copy of the collection instead of modifying the passed one).
template <typename _Ty, typename _By>
LinkedList<_Ty> sort(const LinkedList<_Ty>& source, std::function<bool(_By, _By)> pred)
{
LinkedList<_Ty> tmp;
while (tmp.size() != source.size())
{
_Ty suitable;
for (auto& i : source) {
if (pred(suitable, i) == true) {
suitable = i;
}
}
tmp.push_back(suitable);
}
return tmp;
}
Is my definition of the function wrong? If I try to call the function, I recieve a compilation error.
LinkedList<std::string> strings{
"one",
"two",
"long string",
"the longest of them all"
};
auto sortedByLength = sort(strings, [](const std::string& a, const std::string& b){
return a.length() < b.length();
});
Error: no instance of function template "sort" matches the argument
list argument types are: (LinkedList, lambda []bool
(const std::string &a, const std::string &)->bool)
Additional info, the compilation also gives the following error:
Error 1 error C2784: 'LinkedList<_Ty> sort(const
LinkedList<_Ty> &,std::function)' : could not
deduce template argument for 'std::function<bool(_By,_By)>'
Update: I know the sorting algorithm is incorrect and would not do what is wanted, I have no intention in leaving it as is and do not have a problem fixing that, once the declaration is correct.
The problem is that _By used inside std::function like this cannot be deduced from a lambda closure. You'd need to pass in an actual std::function object, and not a lambda. Remember that the type of a lambda expression is an unnamed class type (called the closure type), and not std::function.
What you're doing is a bit like this:
template <class T>
void foo(std::unique_ptr<T> p);
foo(nullptr);
Here, too, there's no way to deduce T from the argument.
How the standard library normally solves this: it does not restrict itself to std::function in any way, and simply makes the type of the predicate its template parameter:
template <typename _Ty, typename _Pred>
LinkedList<_Ty> sort(const LinkedList<_Ty>& source, _Pred pred)
This way, the closure type will be deduced and all is well.
Notice that you don't need std::function at all—that's pretty much only needed if you need to store a functor, or pass it through a runtime interface (not a compiletime one like templates).
Side note: your code is using identifiers which are reserved for the compiler and standard library (identifiers starting with an underscore followed by an uppercase letter). This is not legal in C++, you should avoid such reserved identifiers in your code.

Resources