Specialized template accepting constructor parameter when only default constructor defined - c++11

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{}};

Related

incomplete type support for list

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.

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>();

Shared pointer to incomplete needs deleter in reset method

I am working with shared_ptr storing pointers of a C library.
Here an example of such a C library containing the header bar.h:
#pragma once
typedef struct Flupp MyFlupp;
MyFlupp *
create_flupp();
void
del_flupp(MyFlupp * fp);
void
print_flupp(MyFlupp * f);
Here the struct has a forward declaration and is defined in the bar.so.
I am using the bar.so in my C++ code:
#include <memory>
extern "C"{
#include "bar.h"
}
int main()
{
std::shared_ptr<MyFlupp> flupp_ptr(nullptr, del_flupp);
flupp_ptr.reset(create_flupp());
print_flupp(flupp_ptr.get());
return 0;
}
Here I am storing the MyFlupp* in a shared_ptr. On the declaration, MyFlupp* is unknown and set to nullptr. Later I am calling the reset operation to set the valid pointer. But when I am compling the code, I get the following error:
In file included from /usr/include/c++/8/bits/shared_ptr.h:52,
from /usr/include/c++/8/memory:81,
from test_foo.cpp:1:
/usr/include/c++/8/bits/shared_ptr_base.h: In instantiation of ‘std::__shared_ptr<_Tp, _Lp>::__shared_ptr(_Yp*) [with _Yp = Flupp; <template-parameter-2-2> = void; _Tp = Flupp; __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2]’:
/usr/include/c++/8/bits/shared_ptr_base.h:1293:4: required from ‘std::__shared_ptr<_Tp, _Lp>::_SafeConv<_Yp> std::__shared_ptr<_Tp, _Lp>::reset(_Yp*) [with _Yp = Flupp; _Tp = Flupp; __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2; std::__shared_ptr<_Tp, _Lp>::_SafeConv<_Yp> = void]’
test_foo.cpp:10:35: required from here
/usr/include/c++/8/bits/shared_ptr_base.h:1126:19: error: invalid application of ‘sizeof’ to incomplete type ‘Flupp’
static_assert( sizeof(_Yp) > 0, "incomplete type" );
When I am providing the deleter to the reset operation than it is working.
flupp_ptr.reset(create_flupp(), del_flupp);
Can anybody explain me whats going on? I already looked #cppreference but I does not found an answer.
The problem is that the type Flupp has only been forward-declared, but not defined. In the context of the use here, it is considered an incomplete type.
This has certain implications for the use with std::shared_ptr:
std::shared_ptr may be used with an incomplete type T. However, the
constructor from a raw pointer (template<class Y> shared_ptr(Y*)) and
the template<class Y> void reset(Y*) member function may only be
called with a pointer to a complete type (note that std::unique_ptr
may be constructed from a raw pointer to an incomplete type).
Source: cppreference.com
Instead you need to use the respective overloads that accept a pointer and the deleter as arguments.
With unique_ptr this is not necessary, as that one stores the custom deleter as part of the type. But with shared_ptr the deleter is type-erased and only recovered at runtime. This allows you to change the deleter of an existing shared_ptr when calling reset. For this reason you always need to re-state which deleter to use whenever you're calling reset. If no deleter is given, each call to reset will also implicitly reset the deleter to just calling delete on the managed pointer.
So to make it work, just change your reset call to
flupp_ptr.reset(create_flupp(), del_flupp);

Can ‘auto’ be used in a function declaration?

Background:
I found this handy random number generator and wanted to make a header file for it:
http://www.cplusplus.com/reference/random/
std::default_random_engine generator;
std::uniform_int_distribution<int> distribution(1,6);
auto dice = std::bind ( distribution, generator );
int wisdom = dice()+dice()+dice();
However, in C++11, a function declaration with return type ‘auto’ requires a trailing return type so the compiler can decide what the type is.
E.g.:
auto foo(int a, int b) -> decltype(a*b);
Problem:
It appears like my header would need to be almost as long as the function itself to determine the type:
std::default_random_engine generator;
std::uniform_int_distribution<int> distribution(1,6);
auto roll() -> decltype(distribution(generator));
Question:
Is there a way around determining the full return type for a function declaration (in a header) that uses the ‘auto’ type?
If not, what should my dice() header look like?
Since you use int as the template type for std::uniform_int_distribution, the return type of distribution(generator) is int. Unless the real code is templated as well, then the return type could be hard-coded to int.
And if the real code is templated then you can use the result_type member of std::uniform_int_distribution:
template<typename T>
typename std::uniform_int_distribution<T>::result_type roll();
Or simply the template type itself:
template<typename T>
T roll();

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.

Resources