This code has a templated class. The default constructor appears to call itself recursively. How can it do that? I don't understand this code. Maybe if I would be given an example without templates, just POD types, things would be clearer. I haven't encountered this construct before in C++ programming. I think that I don't understand both the constructor and the templates.
template <typename T>
class Simple {
public:
Simple(T value = T()); // What's this?
T value();
void set_value(T value);
private:
T value_;
};
template<typename T>
Simple<T>::Simple(T value) {
value_ = value;
}
template<typename T>
T Simple<T>::value() {
return value_;
}
template<typename T>
void Simple<T>::set_value(T value) {
value_ = value;
}
My question is:
What does T value = T() do?
Class Simple has a variable value of type T (Templated).
The constructor which you are pointing is a default constructor. When no parameter is supplied while creating Simple object. Then default constructor will instantiate the value object to the default constructor of T.
Either , Simple(T value = T()) is a default constructor which is instantiating value to default constructor of typed element.
Example :-
if T is String.
Simple (String value = String())
so value is now initialized to default of String().
It is just a default value as void foo(int i = 42);, there are no recursion.
foo(); is equivalent to foo(42);.
In the same way, with Simple(T value = T());
Simple<int> s{}; would be equivalent to Simple<int> s{0}; and
Simple<std::string> s{}; would be equivalent to Simple<std::string> s{std::string{}}; (empty string).
T() would call the default constructor of given T (int and std::string in my examples).
Related
I thought to myself that I don't need std::forward<T>(arg); in my function because I wasn't passing the argument on to another function, I was using it directly. However then I thought even if I use it directly, by for example assigning it, or using it as a constructor argument then those each are function calls, respectively to operator= and constructor, which is a function call:
template <typename element_T>
void push_back(element_t&& copy)
{
*_end = copy; // This calls operator=, which is a function
}
template <typename ... ConstructorArgs>
void emplace_back(ConstructorArgs&& ... args)
{
new (_end) element_t(args);
// Calls constructor, needs (std::forward<ConstructorArgs>(args)...) ?
}
Do I need the calls to std::forward in these cases?
An expression that is a name of a variable is always an lvalue. For example, in
1 template <typename T>
2 void push_back(T&& copy) {
3 *_end = copy;
4 }
copy in line 3 has the lvalue value category no matter what type is deduced for T. Depending on how operator= in line 3 is defined/overloaded, this might result in selecting a wrong overload or in wrong type deduction.
Consider the following simple example (that follows the Ted Lyngmo's example from the comments section):
struct A {
void operator=(const A&); // (1)
void operator=(A&&); // (2)
};
struct C {
template<class T>
void push_back(T&& copy) {
a = copy;
}
A a;
};
C{}.push_back(A{});
Which A's assignment operator will be invoked here? One might expect (2) because A{} is a prvalue, but the correct answer is (1), because copy is an lvalue, and lvalues can't bind to rvalue references.
If we change the assignment to
a = std::forward<T>(copy);
then the expression std::forward<T>(copy) will have the xvalue value category and the (2) assignment operator will be called, as expected.
Placement new follows the same reasoning.
I created this class so I can have a value of any type which is either fixed or recalculated everytime it is used:
template <typename T>
class DynamicValue {
private:
std::variant<T, std::function<T()>> getter;
public:
DynamicValue(const T& constant) : getter(constant){};
template <typename F, typename = std::enable_if_t<std::is_invocable_v<F>>>
DynamicValue(F&& function) : getter(function) {}
DynamicValue(const T* pointer) : DynamicValue([pointer]() { return *pointer; }) {}
DynamicValue(const DynamicValue& value) : getter(value.getter) {}
~DynamicValue() {}
DynamicValue& operator=(const DynamicValue& value) {
getter = value.getter;
return *this;
}
operator T() {
return getter.index() == 0 ? std::get<T>(getter) : std::get<std::function<T()>>(getter)();
}
};
I also wrote the following dummy struct to showcase my issue:
struct A {
int b;
};
The problem is, ideally, I'd be able to treat any DynamicValue<T> as if it were of type T. So if I were to create a new value (DynamicValue<A> a = A{1};), I'd be able to do a.b and get the attribute b of the value calculated by a. However, I get the folowing error:
'class DynamicValue' has no member named 'b'
You can try a live example here.
My question is: is there anyway to overcome this issue, or must I accept the ((A) a).b syntax? I thought about overloading every operator to achieve my goal, but this solution wouldn't work with the . operator (and others), which is not overloadable, and might come with its own issues.
The problem is, ideally, I'd be able to treat any DynamicValue<T> as if it were of type T. So if I were to create a new value (DynamicValue<A> a = A{1};), I'd be able to do a.b and get the attribute b of the value calculated by a.
That would require either operator. overloading or delegated inheritance. Both are not available in C++.
I would recomment two models: operator() overloading or operator*.
Overloading the call operator will make your DynamicValue look like a function, whether it contains a function or a value:
DynamicValue<A> a = A{1};
auto real_a = a();
std::cout << real_a.b;
If you overload the dereference operator, your class would look like a pointer to a value, whether it's a value or a function:
DynamicValue<A> a = A{1};
auto real_a = *a;
std::cout << real_a.b;
I would be careful with operator-> since it may induce significant performance degradation if your dynamic value contains a function.
I would completely avoid implicit conversion operators since it comes with a lot of caveat and gotcha. It's a great footgun and must be implemented with caution.
I wrote some code S s; ... s = {};, expecting it to end up the same as S s = {};. However it didn't. The following example reproduces the problem:
#include <iostream>
struct S
{
S(): a(5) { }
S(int t): a(t) {}
S &operator=(int t) { a = t; return *this; }
S &operator=(S const &t) = default;
int a;
};
int main()
{
S s = {};
S t;
t = {};
std::cout << s.a << '\n';
std::cout << t.a << '\n';
}
The output is:
5
0
My questions are:
Why is operator=(int) selected here, instead of "ambiguous" or the other one?
Is there a tidy workaround, without changing S?
My intent is s = S{}; . Writing s = {}; would be convenient if it worked. I'm currently using s = decltype(s){}; however I'd prefer to avoid repeating the type or the variable name.
Why is operator=(int) selected here, instead of "ambiguous" or the other one?
{} to int is the identity conversion ([over.ics.list]/9). {} to S is a user-defined conversion ([over.ics.list]/6) (technically, it's {} to const S&, and goes through [over.ics.list]/8 and [over.ics.ref] first before coming back to [over.ics.list]/6).
The first wins.
Is there a tidy workaround?
A variation of the trick std::experimental::optional pulls to make t = {} always make t empty.
The key is to make operator=(int) a template. If you want to accept int and only int, then it becomes
template<class Int, std::enable_if_t<std::is_same<Int, int>{}, int> = 0>
S& operator=(Int t) { a = t; return *this; }
Different constraints can be used if you want to enable conversions (you'd probably also want to take the argument by reference in that case).
The point is that by making the right operand's type a template parameter, you block t = {} from using this overload - because {} is a non-deduced context.
...without changing S?
Does template<class T> T default_constructed_instance_of(const T&) { return {}; } and then s = default_constructed_instance_of(s);count?
First of all, the case has nothing to do with the "int" version of the assignment operator, you can just delete it. You can actually delete the other assignment operator too as it will be generated by the compiler. IE this kind of type automatically receives copy/move constructors and the assignment operator. (ie they are not prohibited and you are just repeating what the compiler does automatically with explicit notation)
The first case
uses copy initialization:
S s = {}; // the default constructor is invoked
That is a post-construction copy assignment, yet compilers optimize such simple cases. You should use direction initialization instead:
S s{}; // the default constructor is invoked (as you have it)
Note, you can also write:
S s; // the default constructor is invoked if you have it
The second case
What you should write is direct initialization of the right hand side of the copy assignment:
t = S{};
This notation will invoke the default constructor (if there is one), or value initialization for the members (as long as the type is an aggregate). Here is the relevant info: http://en.cppreference.com/w/cpp/language/value_initialization
I am basically remaking the set container and I am wondering how I pass a brace-enclosed initializer list to the container.
mySet<int> myset = {"test", "test 2"};
I tried to overload the operator= to take a list as a parameter.
Despite the presence of the = here, this is constructing a set, so what you need is a constructor (that takes an std::initializer_list as its parameter).
template<class T>
class myset {
public:
// ...
mySet(std::initializer_list<T> init) {
for (T const &t : init)
insert(t);
}
};
You need to implement a constructor which take a std::initializer_list as its parameter, such as:
template <class T>
class mySet {
public:
mySet(std::initializer_list<T> l) {
// ...
}
};
Define a constructor that takes a std::initializer_list as a parameter, like
mySet(std::initializer_list<T> args){/*...*/}
operator= is invoked only on assignments, not on copy initialization (your case). In this latter case the constructor is invoked on the rhs, followed by a copy (or move), which is usually elided, but nevertheless the copy or move ctors must be accessible.
The print statement in the constructor's definition doesn't get printed, isn't the constructor calling correct in main? I know I am missing some point here, please point out.
#include <iostream>
#include <typeinfo>
template <typename T> class List
{
public:
template <typename T2> List (List<T2> const&);
};
template <typename T> template <typename T2> List <T> :: List (List <T2> const&)
{
std :: cout << "\nType name:" << typeid (T2).name();
}
int main ()
{
List <int> kk (List <int>);
return 0;
}
There are a couple of things wrong in your code that you might not be aware of.
List<int> kk( List<int> );
That line is not a variable definition, but rather the declaration of a function that takes a List<int> as argument and returns a List<int>, so that effectively will not call any constructor. That is know as the most-vexing-parse (you can look at different versions of it by searching in SO, or in the C++ FAQ lite)
The second issue is that you cannot possibly create any instance of the an instantiated type of List, the reason being is that the only constructor that you are providing is a templated constructor that takes a second List<U> as argument. That effectively disables the default constructor, so the only way of creating a List<T> is by already having a List<U>, and that is not possible. You can add the default constructor back:
template <typename T>
class List {
public:
List() {}
template <typename U>
List( List<U> const & ) {} // prefer const& as that will avoid unnecessary copying
};
And now you can write:
List<int> l = List<int>(); // this will call List<int>::List( List<int> const & )
And yet, that will still not call the constructor you want. The reason is a little obscure, but when copy constructing an element of a template, the compiler will not use a templated constructor. In the code above, it will implicitly define a copy constructor by doing member-wise copy constructor of the methods and call that generated constructor. That means that in most occasions where you want to provide a templated constructor you want to also provide a non-templated copy constructor.
To actually call that constructor you would have to provide a different type:
List<int> l = List<double>();
Since the types actually differ, the compiler cannot copy construct, will find that the provided templated constructor is the best overload candidate and call it.
As well as the "most vexing parse" identified by David:
you need to have at least one more constructor to create the original List object to be passed to the copy constructor,
you need to vary the parameter type in order to have the templated copy constructor invoked: as is you'll match the implicitly declared List(const List&) copy constructor instead.
So:
#include <iostream>
template <typename T>
struct X
{
X() { std::cout << "X()\n"; }
// implicitly like this anyway...
// X(const X& rhs) { std::cout << "X(X&)\n"; }
template <typename U>
X(const U& u) { std::cout << "U\n"; }
};
int main()
{
X<int> x;
X<int> y(x);
}
What are you trying to do with this statement:
List <int> kk (List <int>);
(It actually declares a function, and can't be anything but
a function declaration.)
In order to see output from the copy constructor, you've got to
invoke the copy constructor somehow. Which means having an
object to copy. Which isn't possible with the code you've
given: since you've explicitly declared a constructor, the
compiler will not provide a default constructor, and you have no
other constructor with which to create an object. So you have
no way of creating anything to copy. If you add a
List() {}
to the class, and write:
List<int> kk((List<int>());
, you might get something, but the compiler is allowed to elide
the copy here, so more likely there will be no output. Try:
List<int> a;
List<int> b(a);
Or just put your output in the default constructor.