C++ template copy constructor on template class - c++

I have a template class that has a template copy constructor. The problem is when I instantiate this class using another instance of this class with the same template type, my template copy constructor is not called. Why doesn't it match?
Here is the code snippet:
#include <iostream>
template <typename T>
class MyTemplateClass
{
public:
MyTemplateClass()
{
std::cout << "default constructor" << std::endl;
}
/*
MyTemplateClass(const MyTemplateClass<T>& other)
{
std::cout << "copy constructor" << std::endl;
}
*/
template <typename U>
MyTemplateClass(const MyTemplateClass<U>& other)
{
std::cout << "template copy constructor" << std::endl;
}
};
int main()
{
MyTemplateClass<int> instance;
MyTemplateClass<int> instance2(instance);
return EXIT_SUCCESS;
}
The output is
default constructor
But if I explicitly write the default copy constructor (by uncommenting it), then the output becomes
default constructor
copy constructor
I really don't get it. I tested it with my local compiler (Clang 500.2.79) and with this one (GCC 4.9.2) and got the same result.

A copy constructor is of the form X(X& ) or (X const&) and will be provided for you by the compiler if you didn't declare one yourself (or a few other conditions which are not relevant here). You didn't, so implicitly we have the following set of candidates:
MyTemplateClass(const MyTemplateClass&);
template <typename U> MyTemplateClass(const MyTemplateClass<U>&);
Both are viable for
MyTemplateClass<int> instance2(instance);
Both take the same exact arguments. The issue isn't that your copy constructor template doesn't match. The issue is that the implicit copy constructor is not a function template, and non-templates are preferred to template specializations when it comes to overload resolution. From [over.match.best], omitting the unrelated bullet points:
Given these definitions, a viable function F1 is defined to be a better function than another viable function
F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then
— [...]
— F1 is not a function template specialization and F2 is a function template specialization, or, if not that,
— [...]
That's why it calls your implicit (and then, your explicit) copy constructor over your constructor template.

When you do not have a copy constructor in you code, the compiler will implicitly generate it. Therefore when this line is executed:
MyTemplateClass<int> instance2(instance);
A copy constructor is being executed, though obviously not yours. I think that templating has nothing to do with it.
Read more about it here: Implicitly-defined copy constructor

I think REACHUS is right. The compiler is generating a default copy-constructor (as it would with a non-template class too) and preferring this over your template as it's more specialised.
You should make your "normal" copy-constructor private, or better, use the C++11 'deleted' keyword to mark the function as unusable.
However, this doesn't compile. Sorry, I wasn't able to test it at the time.

Related

How do I remove a special constructor from overload resolution?

I'm creating an implementation of C++17's std::optional<class T> in C++14. The specification states that the move constructor should be excluded from overload resolution if T is not move-constructible, and made trivial if T is trivially move-constructible. I'm stuck on getting the former.
Here's a code sample of what I have so far:
template<class T, bool = is_trivially_move_constructible_v<T>>
class optional_move_construct : public optional_copy_construct<T> {
public:
optional_move_construct(optional_move_construct&& rhs) :
optional_base<T>() {
if (rhs.m_full) {
_impl_construct(std::move(rhs.m_value));
}
}
};
optional_copy_construct<T> is part of the inheritance chain I'm using, so I won't worry about it.
There are two ways I can "remove" the move constructor, neither of which works for this scenario.
Option 1: Delete the move constructor. This won't work because deleted functions are included in overload resolution.
Option 2: Use SFINAE to exclude the move constructor from overload resolution. This won't work either, because SFINAE requires a template function to work, and that would be lower-priority than the default move constructor.
How would I go about doing this?
A move constructor has one T&& argument, and possibly additional arguments provided those have default values. That means you can add std::enable_if_t<Condition, int> = 0 as an additional argument to your move constructor.
The compiler won't create a one-argument optional::optional(T&&) move constructor when you have that two-argument move constructor.

Multiple Default Constructors

From this stack overflow question the answer contains this quote:
... definition says that all default constructors (in case there are multiple) ...
How can there be multiple default constructors, and why may that be useful or allowed by the standard?
Default constructors don't have to have no parameters; they just need to be invocable with no arguments.
This condition is fulfilled by any constructor whose arguments all have defaults.
[class.dtor/1]: A default constructor for a class X is a constructor of class X for which each parameter that is not a function parameter pack has a default argument (including the case of a constructor with no parameters). [..]
struct Foo
{
Foo(int a = 0);
Foo(std::string str = "");
};
Now, of course, in this example you cannot actually instantiate a Foo using either of them without providing an argument (the call would be ambiguous). But Foo is still usable, and these are still "default constructors". That's just how the standard has decided to categorise things, for the purpose of defining rules. It doesn't really affect your code or programming.
(By the way, I didn't want to distract but you should have explicit on both of those!)
Here's an example of a class with two default constructors that can be default constructed:
struct Foo
{
Foo(); // #1
template <class... Args>
Foo(Args...); // #2
};
auto test()
{
Foo f{}; // ok calls #1
}

Default constructor invocation difference between C++14 and C++17

Consider the following code (question follows below):
#include <iostream>
struct Type0
{
Type0(char* c)
{}
};
struct Type1
{
Type1(int* i=nullptr) : i_(i)
{}
Type1(const Type1& other) = default;
int* i_;
};
template <typename ...>
struct Composable;
template <typename T0, typename ... T>
struct Composable<T0, T...> : T0, Composable<T...>
{
Composable()
{
std::cout << "Default Invoked: " << sizeof...(T) << std::endl;
}
Composable(const Composable& other) = default;
template<typename Arg, typename ... Args>
Composable(Arg&& arg, Args&& ... args) :
T0(std::forward<Arg>(arg)), Composable<T...>(std::forward<Args>(args)...)
{
std::cout << "Non-default invoked: " << sizeof...(T) << std::endl;
}
};
template <>
struct Composable<>{};
int main()
{
int i=1;
char c='c';
auto comp = Composable<Type0, Type1>(&c, &i);
std::cout << comp.i_ << std::endl;
}
You can find the live code here. This code has an interesting property: depending on whether you compile it with the --std=C++17 or --std=C++14 option, the behaviour changes (you can try this in my link to the live code: edit the g++ invocation --std parameter in the bottom left).
With --std=c++14, you get the following output:
Non-default invoked: 0
Non-default invoked: 1
Default Invoked: 0
Non-default invoked: 1
0x0
With --std=C++17, you get this instead:
Non-default invoked: 0
Non-default invoked: 1
0x7ffcdf02766c
To me, this difference is baffling. It seems clear that the C++17 version is doing the correct thing, and the C++14 is wrong. The C++14 version is invoking the default constructor of both Composable and (from it) Type1 (this is where the 0x0 final line of output comes from, as Type1 provides this as the default value for its i constructor parameter). However, I don't see any place where the default constructor is supposed to be invoked.
Furthermore, if I comment out the default constructor of Composable altogether, the C++17 version does exactly the same thing as before, whereas the C++14 version now fails to compile, complaining about the lack of a default constructor. If there was any hope of the difference being somehow explained by different optimization behavior, this fact surely kills it (the hope was anyhow small as the observed difference persists at all optimization levels, including 0).
Can anyone explain this difference? Is the C++14 behaviour a bug, or some intended behaviour which I do not understand? If the C++14 behaviour is correct within the rules of C++14, can someone please explain where the default constructor calls are coming from?
Guaranteed copy elision.
This line:
auto comp = Composable<Type0, Type1>(&c, &i);
In C++17, this means exactly the same thing as:
Composable<Type0, Type1> comp(&c, &i);
And if you change to this version, you'll see the same behavior bewteen C++14 and C++17. However, in C++14, that is still a move construction (or, more technically correct as you'll see in a minute, copy-initialization). But in Composable, you don't have an implicitly generated move constructor because you have a user-declared copy constructor. As a result, for the move construction, your "Non-default invoked" constructor template gets invoked (it's a better match than the copy constructor) in the C++14 version:
template<typename Arg, typename ... Args>
Composable(Arg&& arg, Args&& ... args) :
T0(std::forward<Arg>(arg)), Composable<T...>(std::forward<Args>(args)...)
Here, Arg is Composable<Type0, Type1> and Args is an empty pack. We delegate to T0 (Type0)'s constructor, forwarding the whole Composable (which works because it inherits from Type0 publicly, so we get the implicitly-generated move constructor there) and to Composable<Type1>'s default constructor (because args is empty).
This constructor template isn't really a proper move constructor - it doesn't end up initializing the Type1 member at all. Instead of moving from the right hand side's Type1::i_ you're invoking Type1::Type1(), the default constructor, which is why you end up with 0.
If you add a proper move constructor:
Composable(Composable&& other) = default;
Then you would again see the same behavior between C++14 and C++17.

decltype() variadic template base class

I have the following code where I'm expecting decltype() to not work on Derived class to get run() base class method return-type, since the base class does not have a default constructor.
class Base
{
public:
int run() { return 1; }
protected:
Base(int){}
};
struct Derived : Base
{
template <typename ...Args>
Derived(Args... args) : Base{args...}
{}
};
int main()
{
decltype(Derived{}.run()) v {10}; // it works. Not expected since
// Derived should not have default constructor
std::cout << "value: " << v << std::endl;
//decltype(Base{}.run()) v1 {10}; // does not work. Expected since
// Base does not have default constructor
//std::cout << "value: " << v1 << std::endl;
}
I'm aware you can use declval<> to get member functions without going through constructors, but my question is why decltype works here. I tried to find in the C++ standard something relevant, but did not find anything. Also tried multiple compilers (gcc 5.2, 7.1 and clang 3.8) and have the same behavior.
Behold the magic of unevaluated contexts... and lying.
Actually trying to do something like:
Derived d;
will be a compile error. It's a compiler error because in the process of evaluating Derived::Derived() we have to invoke Base::Base(), which doesn't exist.
But that's a detail of the implementation of the constructor. In an evaluated context, we certainly need to know that. But in an unevaluated context, we don't need to go so far. If you examine std::is_constructible<Derived>::value, you'd see that is true! This is because you can instantiate that constructor with no arguments - because the implementation of that constructor is outside of the immediate context of that instantiation. This lie - that you can default-construct Derived - allows you to use Derived{} in this context, and the compiler will happily allow you to go on your merry way and see that decltype(Derived{}.run()) is int (which also does not involve actually invoking run, so the body of that function is likewise irrelevant).
If you were honest in the Derived constructor:
template <typename ...Args,
std::enable_if_t<std::is_constructible<Base, Args&...>::value, int> = 0>
Derived(Args&... args) : Base(args...) { }
Then decltype(Derived{}.run()) would fail to compile, because now Derived{} is ill-formed even in an unevaluated context.
It's good to avoid lying to the compiler.
When an expression inside decltype involves a function template, the compiler only looks at the template function's signature to determine whether or not the template could be instantiated if the expression were really in an evaluated context. The actual definition of the function is not used at that point.
(In fact, this is why std::declval can be used inside decltype even though std::declval has no definition at all.)
Your template constructor has the same signature as though simply declared but not yet defined:
template <typename ...Args>
Derived(Args&... args);
While processing the decltype, the compiler just looks at that much information and decides Derived{} is a valid expression, an rvalue of type Derived<>. The : Base{args...} part is part of the template definition and doesn't get used inside decltype.
If you want a compiler error there, you could use something like this to make your constructor more "SFINAE-friendly", which means information about whether or not a specialization of the template is actually valid gets put into the template signature.
template <typename ... Args, typename Enable =
std::enable_if_t<std::is_constructible<Base, Args&...>::value>>
Derived( Args& ... args ) : Base{ args... }
{}
You might also want to modify the constructor to avoid "too perfect forwarding". If you do Derived x; Derived y{x};, the template specialization Derived(Derived&); will be a better match than the implicit Derived(const Derived&);, and you'll end up trying to pass x to Base{x} rather than using the implicit copy constructor of Derived.

c++ why is constructor in this example called twice?

I just try to understand the behaviour of the following situation:
template <typename T1>
struct A{
template <typename T2>
A(T2 val){
cout<<"sizeof(T1): "<<sizeof(T1)<<" sizeof(T2): "<<sizeof(T2)<<endl;
}
T1 dummyField;
};
so - the class is templated with T1 and the constructor is templated with T2
now - if i write:
A<bool> a = A<bool>(true);
the output is as expected:
sizeof(T1): 1 sizeof(T2): 1
however - if i write:
A<bool> a = A<float>(3.5f);
the output is:
sizeof(T1): 4 sizeof(T2): 4
sizeof(T1): 1 sizeof(T2): 4
why is the constructor called twice with template parameter float?
thanks for satisfying my curiosity
How to avoid copying?
In both cases two constructors are called, however you do not see it in the first case as one of them is the compiler generated one. If you want to avoid copying, you need to use a different syntax, like this:
A<bool> a(true);
A<bool> a(3.5f);
Why (and what) copy constructor is called?
A<bool> a = A<bool>(true);
Here the A (bool val) constructor is used to construct the temporary value, while default compiler generated copy constructor is used to perform the copy of A to A. You are copying the same type, and for same type copy constructor is used. The interesting and not obvious fact here is: Template constructor is never used as a copy constructor, even if it looks like one.
A<bool> a = A<float>(3.5f);
Here A<float>(float val) constructor is used first to construct the temporary value, and then A<bool>( A<float> val) templated constructor is used to perform the copy.
in your first example you make a implicit call to the copy constructor
A<bool>(A<bool> const&)
in your second example this wont work as you have two different types
so the compiler has to use your templated constructor to create a new object
declaring
template <typename T2>
A(A<T2>const& val){
cout<<sizeof(val.dummmyField)<<endl;
}
should make this clear
Because you first create a float-instance of the template class.
This is the A<float>(3.5f)-Part.
Then create the A<bool> by covert the A<float> to a A<bool>. So the constructor is first called for the A<float>-instance. The the copy-constructor of A<bool> is called.