Why copy constructor is not invoked? - c++

Sorry for the overly ambiguous title.(due to the lack of my English skill). Please suggest a better title.
Please consider the following code.
struct A {
typedef std::vector<double> State;
// template <class... Args>
// A(Args... args)
// : a(args...)
// {}
template <class... Args>
A(Args&&... args)
: a(std::forward<Args>(args)...)
{}
A(const A&) = default;
A(A&&) = default;
State a;
};
int main(){
A a(3,2);
A b = a; // This line triggers an error!!
}
Gcc 4.8.0 failed to compile it with the error message
error: no matching function for call to 'std::vector<double>::vector(A&)' : a(std::forward<Args>(args)...).
I cannot understand why this code is wrong. In my opinion, the compiler should invoke copy constructor in the line A b = a;.
However if I replace the constructor by the commented one(which simply takes values). It does compile. Furthermore, now the lines for default copy(and move) constructors are not needed.
What happens here?

In C++11 having the compiler automatically deduce template parameters (as you must do with a templated constructor) and applying && to the type creates a universal reference, which matches any type with any cv qualification, whether it's an lvalue or rvalue reference.
So in your case you're passing in an A, and therefore Args...=A &, Args &&...=A & &&, which is A & thanks to reference-collapsing rules, which is a better match than const A &, since the compiler doesn't have to add constness to a non-const variable.

I think in this case the template constructor is a better match because it takes non-const value. if you change a to const it would call the copy constructor...
const A a(3,2);
A b = a;

Related

Conversion constructor strangely not competing with copy constructor

Implementing a copy constructor deletes the default move constructor in C++.
Only compiler generated copy and move constructors are trivial.
Created a templated conversion constructor from any type to the current type.
#include <format>
#include <iostream>
#include <type_traits>
template <typename T = int>
class Element {
public:
T value;
Element(const T value_) noexcept : value(value_) {};
// Here is the conversion constructor
template <typename TT>
Element(const Element<TT> &element) noexcept : value(element.value) {
std::cout << std::format(
"Element<{}>(const {}& {})\n",
typeid(T).name(), typeid(TT).name(), element.value
);
}
// uncommenting this breaks the assertions coming next
// Element(const Element &element) noexcept : value(element.value) {};
};
static_assert(std::is_trivially_move_constructible<Element<>>::value);
static_assert(std::is_trivially_copy_constructible<Element<>>::value);
// how it behaves
void foo_int(Element<int> element) {
std::cout << std::format("foo_int: {}\n", element.value);
}
void foo_double(Element<double> element) {
std::cout << std::format("foo_double: {}\n", element.value);
}
int main() {
Element<int> int_element {1};
Element<double> double_element {1.5};
foo_int(int_element);
foo_double(int_element);
// uncommenting doesn't compile - narrowing conversion
// foo_int(double_element);
foo_double(double_element);
return 0;
}
Demo in Compiler Explorer
Can someone explain to me why this conversion constructor is not matched with T == TT. In that case only the copy / move constructor is called.
Then I thought maybe I can call it manually with auto a = Element<int>::Element<int>(int_element);. But that gives an error: "obsolete declaration style".
It seems to be treated like a normal constructor, and only considered after the other special member functions.
Can someone explain me the rules or where I can read more about this behavior?
When type T is the same as TT, then you are making a copy. The standard explicitly states that the copy constructor is not a template:
non-template constructor for class X is a copy constructor if its
first parameter is of type X&, const X&, volatile X& or const volatile
X&, and either there are no other parameters or else all other
parameters have default arguments ([dcl.fct.default]).
https://eel.is/c++draft/class.copy.ctor#1
Also, in C++, a non-template is always preferred over a template if it's an exact match. Given there is a real copy constructor, and your templated conversion constructor, in copying contexts the copy constructor would be picked. The same thing would happen for ordinary functions too:
template <typename T>
void foo(T);
void foo(int);
foo(123); // will call the non-template version (since it's an exact match)

How to write a copy constructor for a template containing std::tuple which is not always copiable

I have a class template that has a std::tuple field, like:
template <typename Fp, typename... ArgRefs> class func_wrapper {
std::decay_t<Fp> func;
std::tuple<ArgRefs...> args;
public:
func_wrapper(Fp f, ArgRefs... args)
: func(f), args(std::forward<decltype(args)>(args)...) {}
func_wrapper(const func_wrapper &rhs) noexcept
: func(rhs.func), args(rhs.args) {}
func_wrapper(func_wrapper &&rhs) noexcept
: func(std::move(rhs.func)), args(std::move(rhs.args)) {}
auto operator()() { /* ... */ }
};
The purpose of this class is to turn a function of arbitrary parameters to a function of empty parameter list. So I stored the references of the arguments to the field args.
The problem is, the args tuple may contain lvalue references and rvalue references, which makes this tuple not copyable when it contains rvalue references. This causes the copy constructor to fail.
void foo(int &a1, int &&a2) {
a1 = a2 + 1;
}
void test(int x) {
using F = decltype(foo);
auto fw1 = func_wrapper<F, int &, int &&>(foo, x, 10);
// Error
auto fw2 = fw1;
// Success
auto fw3 = std::move(fw1);
}
$ clang++ test.cpp -c
test.cpp:13:23: error: call to implicitly-deleted copy constructor of 'std::tuple<int &, int &&>'
: func(rhs.func), args(rhs.args) {}
^ ~~~~~~~~
In this case, the args tuple has an int && element, which makes the tuple not able to be copy-initialized. But I want the func_wrapper to be copyable. So, is there any way that I can re-implement the copy constructor to make this class copyable?
It seems that the move constructor can always work. But unfortunately, I can not always move the object of this class. A library function I call requires the object to be copied.
Currently I work around this problem by forcing moving the tuple in the copy constructor:
func_wrapper(const func_wrapper &rhs) noexcept
: func(rhs.func), args(std::move(const_cast<func_wrapper &>(rhs).args)) {}
By const_cast and std::move, I can use tuple's move constructor to initialize the args member. Not quite elegent but the problem disappears. Comments are welcomed.

non-template std::reference_wrapper assignment operator and template constructor

In the C++ 20 Standard the constructor of the class template std::reference_wrapper is a template.
template<class U>
constexpr reference_wrapper(U&&) noexcept(see below );
while the assignment operator is not a template
constexpr reference_wrapper& operator=(const reference_wrapper& x) noexcept;
What is the reason that this difference (template and non-template) between these special member functions exists?
On the other hand, I tried the following progarm using Visual C++ 2019.
#include <iostream>
#include <functional>
struct A
{
void f() const { std::cout << "A::f()\n"; }
virtual void g() const { std::cout << "A::g()\n"; }
};
struct B : A
{
void f() const { std::cout << "B::f()\n"; }
void g() const override { std::cout << "B::g()\n"; }
};
int main()
{
B b;
std::reference_wrapper<A> r( b );
r.get().f();
r.get().g();
r = std::reference_wrapper<B>( b );
}
and the compiler did not issue an error message relative to the assignment operator.
Is it a bug of the compiler or have I missed something?
If std::reference_wrapper<T> had a single constructor accepting T&, then it would lead to bugs like std::reference_wrapper<const T> being able to bind to temporaries of type T. So, originally there were two constructors (other than the copy constructor): one taking T&, and another taking T&&, which was defined as deleted, ensuring that a compile error would occur.
However, it was pointed out that this is not really what we want: it would be better if std::reference_wrapper<T> would have no constructor at all that accepts an rvalue of type T, as opposed to a deleted constructor. This is LWG 2993. (You'll notice that this issue is mentioned at the bottom of the cppreference page.) Thus, the constructor was changed to a template that is SFINAE-disabled as appropriate.
Once these issues are solved for the constructor, the assignment operator only needs to have a single overload taking reference_wrapper. The conversion issues will be handled by the constructor logic when the compiler forms the implicit conversion sequence.

Why can't boost::hana::overload_t be a member of a class

So I have a couple of functions, which I get a magic function object, that provides overload resolution on:
void foo1();
void foo2(int);
auto foo_ptr = boost::hana::overload(foo1,foo2);
//Later
foo_ptr(12); //Both Valid (Yeah!)
foo_ptr();
But the problem arises when I do something like this:
using ptr_t = decltype(foo_ptr);
struct mine
{
ptr_t ptr;
mine(ptr_t ptr) : ptr(ptr){}
};
mine m(foo_ptr);
When I try to compile this code I get error: no matching function for call to 'boost::hana::overload_t<void (*)(int)>::overload_t()'.
See for yourself on godbolt.....
Now my question is this:
Am I allowed to copy these overload objects around (the documentation for hana does not say one way or the other), and if so why does it fail when I put it in a class as a member?
This is arguably a bug in Boost.Hana. The constructor of overload_t is:
template <typename F_, typename ...G_>
constexpr explicit overload_t(F_&& f, G_&& ...g)
: overload_t<F>::type(static_cast<F_&&>(f))
, overload_t<G...>::type(static_cast<G_&&>(g)...)
{ }
Notice that these are unconstrained forwarding references. This is the common problem of having a forwarding reference constructor be a better match than the copy constructor, when you have a non-constant object. Reduced example:
struct X {
X();
template <typename F> X(F&&);
X(X const&) = default;
};
X x1;
X x2(x1); // does *not* call the copy constructor
However, if the object were const, then the copy constructor would be a better match. So the short fix is to just do that:
struct mine
{
ptr_t ptr;
mine(ptr_t const& ptr) : ptr(ptr){}
};
And now it compiles. Likewise, as Yakk suggests, move instead of copy would also work for the same reason:
struct mine
{
ptr_t ptr;
mine(ptr_t ptr) : ptr(std::move(ptr)) {}
};

std::forward and copy constructor

I have started leaning C++ recently. And I have a question on std::forward with lvalue reference and rvalue reference.
In my understanding, The Func(mc) in the following code suppose to call
Func(T& t) because of template parameter deduction rule. And copy constructor of MyClass should be called inside the function with a message, "copy constructor". However I cannot get it when I run the program.
I checked that std::forwad with rvalue reference and copy constructor is working well in other lines.
Please help me to understand what happen in the code. If I make easy mistake or misunderstanding, I am sorry for taking your time. Thank you very much.
class MyClass {
public:
MyClass() { printf("constructor.\n"); };
MyClass(MyClass&) { printf("copy constructor.\n"); };
MyClass(const MyClass&) { printf("const copy constructor.\n"); };
MyClass(MyClass&&) { printf("move constructor.\n"); };
int val = 3;
};
template <typename T>
void Func(T&& t) {
T new_t_(std::forward<T>(t));
new_t_.val *= 2;
};
main() {
MyClass mc;
Func(mc); // lvalue <- Func(T&)
Func(MyClass()); // rsvalue <- Func(T&&)
printf("mc.val=%d\n", mc.val); // this is for check
MyClass mc2(mc); // this is for check of copy constructor
}
Output when I run program is following,
constructor.
constructor.
move constructor.
mc.val=6
copy constructor.
I think it should have "copy constructor" between the first and the second "constructor" messages.
Thank you very much again.
Func(mc);
This call will deduce T to be MyClass&. Note the reference. This is due to the way perfect forwarding has been introduced into C++: A forwarding reference such as the T&& function parameter of Func will be deduced to be an lvalue reference if the corresponding function argument is an lvalue-expression; otherwise (for xvalues and prvalues) it will not be deduced to be a reference type.
The type deduced for T will then be reference-collapsed with the && from the function parameter T&& t to form the final function parameter type. In the case Func(mc), T == MyClass&, so the function parameter becomes MyClass& &&, collapsed to MyClass&.
Since T == MyClass&, the declaration of the local variable new_t_ will declare a reference in this case. No new object will be created:
template <>
void Func<MyClass&>(MyClass& t) {
MyClass& new_t_(std::forward<MyClass&>(t));
new_t_.val *= 2;
}
Func(MyClass());
Here, the function argument is the prvalue-expression MyClass(). T will be deduced to MyClass (without reference). The function template instantiation produced looks like this:
template <>
void Func<MyClass>(MyClass&& t) {
MyClass new_t_(std::forward<MyClass>(t));
new_t_.val *= 2;
}
If you want to copy/move the function argument into a local variable, the std::decay metafunction can be used - or, more specific to the OP's problem, the std::remove_reference metafunction. E.g.
template <typename T>
void Func(T&& t) {
using DT = typename std::decay<T>::type;
DT new_t_(std::forward<T>(t));
new_t_.val *= 2;
}