template constructor overloading issue - c++

I have a class template with three constructors and one of them is function template.
template<class T>
class TemplateOverLoading
{
public:
TemplateOverLoading(void){};
~TemplateOverLoading(void){};
//constructor that take reference
TemplateOverLoading(std::string& qName, T& qValue )
: mName(qName),
mValue( &qValue)
{
std::cout << "Reference -> "<< *mValue <<"\n";
}
//Template constructor that takes array
template<class T, int N>
TemplateOverLoading(std::string& qName, T (&t)[N])
: mName(qName),
mValue(t)
{
std::cout << "Array ->\n";
for(int i = 0; i < N; i++)
std::cout<< mValue[i];
std::cout << std::endl;
}
//Other constructor that take pointer
TemplateOverLoading(std::string& qName, T* qValue )
: mName(qName),
mValue( qValue)
{
std::cout << "Pointer "<< *mValue <<"\n";
}
private:
T* mValue;
//T* mValueArray;
std::string& mName;
};
From my application I need to distinguish between reference type/value, pointer and array and perform specific operation. Hence I decided to have to different constructors.
I am trying to invoke the constructor in the following manner:
int init(10);
int intArray[10] = {0,1,2,3,4,5,6,7,8,9};
TemplateOverLoading<int> mInt(std::string("mInt"), init);
TemplateOverLoading<int> mIntArray( std::string("mIntArray"), intArray );
The problem is if the constructor with the pointer is defined the array constructor is never invoked. However, if I comment that one out it prints the array as it should.
Output:
(when pointer constructor is present)
Reference -> 10
Pointer 0
(when pointer constructor is not present)
Reference -> 10
Array ->
0123456789
So syntactically it is possible and it is correct deducing the the array size N.
Obviously I am confusing the compiler when the array constructor is present. So instead of letting the compiler automatically deduce I tried to specify the template parameter for the array constructor to only find out unlike the regular function, template parameters cannot be specifically specified.
I thought of introducing a dummy parameter in the array constructor to differentiate the overloading but it doesn't seem nice.
Is there any other way to resolve this? Any clue appreciated.

Make your pointer constructor parameter to T*& qValue.
TemplateOverLoading(std::string& qName, T*& qValue )
: mName(qName),
mValue( qValue)
{
std::cout << "Pointer "<< *mValue <<"\n";
}
By making it a reference to a pointer, array-to-pointer decay is prevented and the array constructor is chosen.
Also, I don't see how your code compiles, I see many errors:
Your template constructor has a parameter class T, this conflicts with the class T of the class template:
template<class T, int N>
TemplateOverLoading(std::string& qName, T (&t)[N])
This needs to be changed to something else than class T, for example class U:
template<class U, int N>
TemplateOverLoading(std::string& qName, U (&t)[N])
Your constructors also take a non-const lvalue reference, non const reference cannot bind to temporaries, which you are passing in the constructor call, i.e. std::string("mIntArray"). You need to change it to const std::string& or take it by value. Also your member std::string& mName; is a reference, you should remove the & there.

Related

Adding an object to std::map doesn't work unless an "empty" (no args) constructor for the class' object exists

I am studying the code of an open source app. I have made a simpler version of this code to isolate something bugging me (though I have several questions with this code that I am hoping C++ gurus will be able to help me with, I will start with the main one).
Main question: why do I need an "empty" constructor (no arguments) for the class of an object that's being assigned to a std::map?
The main idea (see code below) is to assign an instance of the Variant class to a std::map (whose key is a std::string). Here is the code:
#include <iostream>
#include <map>
#include <string>
#include <memory>
struct Data
{
public:
Data(const void* data, size_t bytes) : bytes(bytes)
{
ptr = malloc(bytes);
memcpy(ptr, data, bytes);
std::cout << "in ctor of Data" << std::endl;
}
~Data() { free(ptr); std::cout << "in dtor of Data" << std::endl; }
void* ptr{ nullptr };
size_t bytes;
};
struct DataStream
{
public:
DataStream(const std::shared_ptr<Data>& ptr, size_t size) : ptr(ptr), size(size)
{ std::cout << "in ctor of DataStream" << std::endl; }
std::shared_ptr<Data> ptr;
size_t size;
};
struct Variant
{
public:
enum Type
{
EMPTY,
TYPE1 = 5,
TYPE2 = 10
};
~Variant() { std::cout << "in dtor of Variant" << std::endl; }
// XXX If this ctor does NOT exist, the code doesn't compile XXX
Variant() : type(EMPTY) { std::cout << "in ctor of Variant" << std::endl; }
Variant(const int& n, Type type) : n(n), type(type) {}
Variant(const std::shared_ptr<Data>& data, Type type, size_t size) : type(type), data(std::make_shared<DataStream>(data, size))
{ std::cout << "in ctor of Variant (ptr to typed array)" << std::endl; }
Type type;
int n;
std::shared_ptr<DataStream> data;
};
struct Params
{
public:
void add(const std::string& name, const Variant& data) { params[name] = data; }
const Variant& operator[] (const std::string& name) { return params[name]; }
std::map<std::string, Variant> params;
};
struct Handle
{
public:
Params params;
void set(const std::string& name, const Variant& data) { params.add(name, data); }
};
int main()
{
Handle* handle = new Handle();
char data_i[3] = { 'a', 'b', 'c' };
std::shared_ptr<Data> data = std::make_shared<Data>(data_i, 3);
handle->set("testC", Variant(data, Variant::TYPE1, 3));
std::cout << "use_count data " << handle->params["testC"].data->ptr.use_count() << std::endl;
std::cout << "Variant type " << handle->params["testC"].type << std::endl;
delete handle;
return 0;
}
If I don't add to the class a constructor that doesn't take any arguments (what I call an empty constructor) the code doesn't compile. I get the following error msg:
test3.cpp:52:68: note: in instantiation of member function 'std::map<std::basic_string<char>, Variant>::operator[]' requested here
void add(const std::string& name, const Variant& data) { params[name] = data; }
^
test3.cpp:29:8: note: candidate constructor (the implicit copy constructor) not viable: requires 1 argument, but 0 were provided
struct Variant
^
test3.cpp:40:5: note: candidate constructor not viable: requires 2 arguments, but 0 were provided
Variant(const int& n, Type type) : n(n), type(type) {}
^
test3.cpp:41:5: note: candidate constructor not viable: requires 3 arguments, but 0 were provided
Variant(const std::shared_ptr<Data>& data, Type type, size_t size) : type(type), data(std::make_shared<DataStream>(data, size))
^
My understanding of what's going on is limited here. I do get that I am assigning an instance of the class Variant to a map and that an instance has to be created at this point of the code. Therefore the ctor of the object's class will be called. Makes sense. And since it has no argument it needs to use a constructor that does not take any argument. Fair enough. So I add the line for the constructor with no argument. The code compiles. Then, I output the type of the object stored in the map and I can see that even though the constructor with no argument was called for the creation of that object, it still has the type of the original object (5 rather than 0).
So this is the bit for which I'd like to have an explanation. How can it actually copy the content of the original object (the one that's created in main()) even though the constructor with no argument when assigning a copy of that object to std::map was used?
Subsidiary question #1 ):
If I look at the sequence of constructor/destructor for that Variant object I get the following:
in ctor of Data
in ctor of DataStream
in ctor of Variant (ptr to typed array)
in ctor of Variant
in dtor of Variant
use_count data 2
Variant type 5
in dtor of Variant
in dtor of Data
In the original code, the add method is:
void add(const std::string& name, Variant data) { params[name] = data; }
The const ref is not even there. And the sequence is:
in ctor of Data
in ctor of DataStream
in ctor of Variant (ptr to typed array)
in ctor of Variant
in dtor of Variant
in dtor of Variant
use_count data 2
Variant type 5
in dtor of Variant
in dtor of Data
The destructor of Variant is called 3 times but the constructors only twice! Not sure which constructor I am missing in this case.
But anyway, my question is: from the moment I create the temporary variable here:
handle->set("testC", Variant(data, Variant::TYPE1, 3));
Can I somehow insure that no copies of that object are made until I assign it to map? I have tried to add a bunch of std::move there & there, but it doesn't seem to make a difference. I am just thinking that these copies are not necessarily mandatory and that there must be a way of avoiding them. Your guru's input would be greatly appreciated for these 2 questions.
operator[] requires that the type is DefaultConstructible, if the key does not exist.
On line params[name] = data;
operator[] creates an element using the default constructor
operator[] returns a reference to the element
data is assigned to the reference, using the copy constructor
In your case, the step 1 fails because there is no default constructor.
C++17 adds insert_or_assign(), which does not require the type to be DefaultConstructible.

std::tuple for non-copyable and non-movable object

I have a class with copy & move ctor deleted.
struct A
{
A(int a):data(a){}
~A(){ std::cout << "~A()" << this << " : " << data << std::endl; }
A(A const &obj) = delete;
A(A &&obj) = delete;
friend std::ostream & operator << ( std::ostream & out , A const & obj);
int data;
};
And I want to create a tuple with objects of this class. But the following does not compile:
auto p = std::tuple<A,A>(A{10},A{20});
On the other hand, the following does compile, but gives a surprising output.
int main() {
auto q = std::tuple<A&&,A&&>(A{100},A{200});
std::cout << "q created\n";
}
Output
~A()0x22fe10 : 100
~A()0x22fe30 : 200
q created
It means that dtor for objects is called as soon as tuple construction line ends. So, what is significance of a tuple of destroyed objects?
This is bad:
auto q = std::tuple<A&&,A&&>(A{100},A{200});
you are constructing a tuple of rvalue references to temporaries that get destroyed at the end of the expression, so you're left with dangling references.
The correct statement would be:
std::tuple<A, A> q(100, 200);
However, until quite recently, the above was not supported by the standard. In N4296, the wording around the relevant constructor for tuple is [tuple.cnstr]:
template <class... UTypes>
constexpr explicit tuple(UTypes&&... u);
Requires: sizeof...(Types) == sizeof...(UTypes). is_constructible<Ti, Ui&&>::value is true
for all i.
Effects: Initializes the elements in the tuple with the corresponding value in std::forward<UTypes>(u).
Remark: This constructor shall not participate in overload resolution unless each type in UTypes is
implicitly convertible to its corresponding type in Types.
So, this constructor was not participating in overload resolution because int is not implicitly convertible to A. This has been resolved by the adoption of Improving pair and tuple, which addressed precisely your use-case:
struct D { D(int); D(const D&) = delete; };
std::tuple<D> td(12); // Error
The new wording for this constructor is, from N4527:
Remarks: This constructor shall not participate in overload resolution unless sizeof...(Types) >= 1 and is_constructible<Ti, Ui&&>::value is true for all i. The constructor is explicit if and only
if is_convertible<Ui&&, Ti>::value is false for at least one i.
And is_constructible<A, int&&>::value is true.
To present the difference another way, here is an extremely stripped down tuple implementation:
struct D { D(int ) {} D(const D& ) = delete; };
template <typename T>
struct Tuple {
Tuple(const T& t)
: T(t)
{ }
template <typename U,
#ifdef USE_OLD_RULES
typename = std::enable_if_t<std::is_convertible<U, T>::value>
#else
typename = std::enable_if_t<std::is_constructible<T, U&&>::value>
#endif
>
Tuple(U&& u)
: t(std::forward<U>(u))
{ }
T t;
};
int main()
{
Tuple<D> t(12);
}
If USE_OLD_RULES is defined, the first constructor is the only viable constructor and hence the code will not compile since D is noncopyable. Otherwise, the second constructor is the best viable candidate and that one is well-formed.
The adoption was recent enough that neither gcc 5.2 nor clang 3.6 actually will compile this example yet. So you will either need a newer compiler than that (gcc 6.0 works) or come up with a different design.
Your problem is that you explicitly asked for a tuple of rvalue references, and a rvalue reference is not that far from a pointer.
So auto q = std::tuple<A&&,A&&>(A{100},A{200}); creates two A objects, takes (rvalue) references to them, build the tuple with the references... and destroys the temporary objects, leaving you with two dangling references
Even if it is said to be more secure than good old C and its dangling pointers, C++ still allows programmer to write wrong programs.
Anyway, the following would make sense (note usage of A& and not A&&):
int main() {
A a(100), b(100); // Ok, a and b will leave as long as main
auto q = tuple<A&, A&>(a, b); // ok, q contains references to a and b
...
return 0; // Ok, q, a and b will be destroyed
}

Move and Forward cases use

I followed this tutorial to start to understand the move semantics and rvalue references in C++11.
At some point, he implements these two classes with the std::move in the move constructors explaining that
we pass the temporary to a move constructor, and it takes on new life
in the new scope. In the context where the rvalue expression was
evaluated, the temporary object really is over and done with. But in
our constructor, the object has a name; it will be alive for the
entire duration of our function. In other words, we might use the
variable other more than once in the function, and the temporary
object has a defined location that truly persists for the entire
function. It's an lvalue in the true sense of the term locator value
class MetaData
{
public:
MetaData(int size, const string& name)
: _name(name)
, _size(size)
{}
MetaData(const MetaData& other)
: _name(other._name)
, _size(other._size)
{
cout << "MetaData -- Copy Constructor" << endl;
}
MetaData(MetaData&& other)
: _name(move(other._name))
, _size(other._size)
{
cout << "MetaData -- Move Constructor" << endl;
}
~MetaData()
{
_name.clear();
}
string getName() const { return _name; }
int getSize() const { return _size; }
private:
string _name;
int _size;
};
class ArrayWrapper
{
public:
ArrayWrapper()
: _p_vals(new int[64])
, _metadata(64, "ArrayWrapper")
{}
ArrayWrapper(int n)
: _p_vals(new int[n])
, _metadata(n, "ArrayWrapper")
{}
ArrayWrapper(ArrayWrapper&& other)
: _p_vals(other._p_vals)
, _metadata(move(other._metadata))
{
cout << "ArrayWrapper -- Move Constructor" << endl;
other._p_vals = nullptr;
}
ArrayWrapper(const ArrayWrapper& other)
: _p_vals(new int[other._metadata.getSize()])
, _metadata(other._metadata)
{
cout << "ArrayWrapper -- Copy Constructor" << endl;
for (int i = 0; i < _metadata.getSize(); ++i)
_p_vals[i] = other._p_vals[i];
}
~ArrayWrapper()
{
delete[] _p_vals;
}
int* getVals() const { return _p_vals; }
MetaData getMeta() const { return _metadata; }
private:
int* _p_vals;
MetaData _metadata;
};
In the ArrayWrapper move constructor I tried to change std::move with std::forward<MetaData> and the code shows that if I call the ArrayWrapper move constructor this will call the MetaData move constructor, like the example with the std::move.
Of course if I don't use either std::move or std::forward the MetaData copy costructor will be called.
The question is, in this case, is there a difference between using std::move and std::forward? Why should I use one instead of the other?
is there a difference between using std::move and std::forward? Why should I use one instead of the other?
Yes, std::move returns an rvalue reference of its parameter, while std::forward just forwards the parameter preserving its value category.
Use move when you clearly want to convert something to an rvalue. Use forward when you don't know what you've (may be an lvalue or an rvalue) and want to perfectly forward it (preserving its l or r valueness) to something. Can I typically/always use std::forward instead of std::move? is a question you might be interested in here.
In the below snippet, bar would get exactly what the caller of foo had passed, including its value category preserved:
template <class T>
void foo(T&& t) {
bar(std::forward<T>(t));
}
Don't let T&& fool you here - t is not an rvalue reference. When it appears in a type-deducing context, T&& acquires a special meaning. When foo is instantiated, T depends on whether the argument passed is an lvalue or an rvalue. If it's an lvalue of type U, T is deduced to U&. If it's an rvalue, T is deduced to U. See this excellent article for details. You need to understand about value categories and reference collapsing to understand things better in this front.
The relevant std::forward and std::move declarations are:
template< class T >
T&& forward( typename std::remove_reference<T>::type& t );
template< class T >
typename std::remove_reference<T>::type&& move( T&& t );
For the former:
std::forward<MetaData>(other._metadata);
std::forward<MetaData> returns MetaData&&.
For the latter:
std::move(other._metadata);
//argument derived as lvalue reference due to forwarding reference
std::move<MetaData&>(other._name);
std::move<MetaData&> returns typename std::remove_reference<MetaData&>::type&&, which is MetaData&&.
So the two forms are identical for your example. However, std::move is the right choice here, as it shows our intent to unconditionally move the argument. std::forward can be used to unconditionally move, but the purpose of it is to perfect-forward its argument.

How to store either rvalue or lvalue references in template

I am trying to create a simple template enumerator class that should accept any object over which : operator is defined and later on print pairs of the form (i, v[i]). A simple implementation is the following:
template<typename T>
struct enumerator {
T &v; // reference to minimize copying
enumerator(T &_v) : v(_v) {}
void do_enumerate() {
size_t i = 0;
for(auto x : v) {
cout << i << x << endl;
i++;
}
}
};
This works ok for things like:
Case A
vector<int> v({1,2,6,2,4});
auto e = enumerator(v);
e.do_enumerate();
However, I would also like it to handle temporary objects like:
Case B
auto e = enumerator(vector<int>({2,3,9});
e.do_enumerate();
This does not work and the compiler throws:
no matching function for call to ‘enumerator<std::vector<int> >::enumerator(std::vector<int>)
So, I tried to then add a
enumerator(T _t) : t(_T) {}
constructor to resolve this error. Now case A does not work and there is an error:
error: call of overloaded ‘enumerator(std::vector<int>&)’ is ambiguous
Moreover, in case B, the output of the enumeration is not correct.
What is the cleanest way to solve this? I would
really like both cases to be working
prefer not to use any libraries other than stdc++
want as little copying as possible (thus just storing a T t in the struct is not an option)
C++11 is not a problem. I have g++-4.8, which I presume has complete enough C++11 support.
Well I would like to copy in case the argument is an rvalue, and not copy in case it is not. Is that possible?
This can be accomplished using a make_enumerator helper function as shown.
template <class T>
struct enumerator {
T v;
enumerator(T&& _v) : v(std::forward<T>(_v)) {}
void do_enumerate() {
size_t i = 0;
for(auto x : v) {
cout << i << x << endl;
i++;
}
}
};
template <class T>
enumerator<T> make_enumerator(T&& x) {
return enumerator<T>(std::forward<T>(x));
}
int main() {
vector<int> v {5, 2, 9, 1};
make_enumerator(v).do_enumerate();
make_enumerator(std::move(v)).do_enumerate();
}
How does this work?
If the argument to make_enumerator is an lvalue of type A then T is deduced as A& and we get the enumerator enumerator<A&>, whereas if it is an rvalue of type A then T is deduced as A and we get the enumerator enumerator<A>.
In the first case the member enumerator::v will have type A&, an lvalue reference that binds to the constructor argument (no copying). In the second case the member will have type A. The use of std::forward casts the parameter _v to an rvalue, so it will be moved from when it is used to initialize v.
This is a classic example where you don't actually need a class/struct (which actually introduce useless code) and you can just use good old functions:
template<typename Container>
void enumerate(const Container& t) {
std::size_t i = 0;
for(auto it = t.begin(); it != t.end(); ++it, ++i)
std::cout << i << *it << std::endl;
}
and then call it as:
enumerate(std::vector<int>{2,3,9});
Live demo
With this method you also get argument type inference for free (which you don't get with a struct).

C++ templated function wrapper arguments using const& versus &

Creating a wrapper function for a constructor such as the following compiles just fine:
#include <iostream>
template <typename T>
class wrapper
{
public:
template <typename A0>
T* operator () (const A0& a0) const
{
return new T(a0);
}
};
class Foo
{
public:
Foo(int i) { std::cout << "Foo:Foo(" << i << ")" << std::endl; }
};
int main(int argc, char** argv)
{
wrapper<Foo>()(42);
return 0;
}
But the code does not compile when I update the line:
T* operator () (const A0& a0) const
to:
T* operator () (A0& a0) const
My guess is this has to do with the rvalue '42' not being bindable to to a non-const reference. But when I make the reference const this will mean that I could never call a constructor that actually takes a non-const reference. Can someone explain what is going on here, and what is the right thing to do to make it work?
My guess is this has to do with the rvalue '42' not being bindable to to a non-const reference.
Yes. Correct.
But when I make the reference const this will mean that I could never call a constructor that actually takes a non-const reference.
No. Incorrect. You can still call it with non-const reference. In fact, that is how const-ness works : non-const reference/pointer/object can implicitly convert into const reference/pointer/object, but vice-versa is not true.
So try it out.