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)
Related
I've designed a simpler wrapper class that adds a label to an object, with the intent of being implicitly convertible/able to replace the wrapped object.
#include <string>
#include <type_traits>
#include <utility>
template < typename T, typename Key = std::string >
class myTag{
T val;
public:
Key key;
template < typename... Args,
typename = typename std::enable_if< std::is_constructible< T, Args... >::value >::type >
myTag(Args&&... args) :
val(std::forward< Args >(args)...) {
std::cout << "forward ctor" << std::endl;
}
myTag(const myTag& other) :
key(other.key), val(other.val) {
std::cout << "copy ctor" << std::endl;
}
myTag(myTag&& other):
key(other.key), val(other.val) {
std::cout << "move ctor" << std::endl;
}
operator T&() { return val; }
operator const T&() const { return val; }
};
int main(int argc, char const *argv[]) {
myTag< float > foo(5.6); // forward ctor
myTag< float > bar(foo); // forward ctor
return 0;
}
However, I am having trouble properly declaring & defining the copy/move constructor. I declared a generic constructor overload template that forwards its arguments to the underlying type, as long as such construction is possible. However, due to the implicit conversion operators, it is capturing every instantiation of myTag, effectively shadowing the copy/move constructors.
The point of non-default copy/move semantics was to copy/move the key value vs default-initializing it with constructor template.
How do I make the compiler prefer/give precedence to the explicit copy/move constructors vs the generic overload? Is there any additional SFINAE check alternative to is_constructible<> that avoids implicit conversions?
EDIT: I should add that I'm looking for a C++14 solution.
It isn't shadowing the copy and move constructors. It is just beating them in overload resolution in some cases.
If you pass a myTag<float>&, the forwarding constructor is used.
If you pass a const myTag<float>& the copy constructor is used.
If you pass a myTag<float>&&, the move constructor is used.
If you pass a const myTag<float>&&, the forwarding constructor is used.
The copy and move constructors are not templates, so they will win against a template of the same signature. But the forwarding constructor can be a better match in the cases where the deduced signature differs from the copy and move constructors.
The idiomatic way to handle this is to remove the forwarding constructor from consideration based on the decayed type of the arguments:
template <typename... Args>
constexpr myTag(Args&&... args)
requires
((sizeof...(Args) != 1 && std::is_constructible_v<T, Args...>) ||
(sizeof...(Args) == 1 && std::is_convertible_v<Args..., T> && !std::is_same_v<myTag, std::decay_t<Args>...>))
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.
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;
}
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;
In the following code, the variadic constructor is called twice. How can I get the copy constructor to be called instead of the single argument version of the variadic constructor when appropriate?
#include <iostream>
struct Foo
{
Foo(const Foo &)
{
std::cout << "copy constructor\n";
}
template<typename... Args>
Foo(Args&&... args)
{
std::cout << "variadic constructor\n";
}
std::string message;
};
int main()
{
Foo f1;
Foo f2(f1); // this calls the variadic constructor, but I want the copy constructor.
}
This actually has nothing to do with the fact that the constructor is variadic. The following class with a non-variadic constructor template exhibits the same behavior:
struct Foo
{
Foo() { }
Foo(const Foo& x)
{
std::cout << "copy constructor\n";
}
template <typename T>
Foo(T&& x)
{
std::cout << "template constructor\n";
}
};
The problem is that the constructor template is a better match. To call the copy constructor, a qualification conversion is required to bind the non-const lvalue f1 to const Foo& (the const qualification must be added).
To call the constructor template, no conversions are required: T can be deduced to Foo&, which after reference collapsing (Foo& && -> Foo&), gives the parameter x type Foo&.
You can work around this by providing a second copy constructor that has a non-const lvalue reference parameter Foo&.
Just provide an exact-match overload, i.e. one with a non-const Foo&, in addition to the conventional copy constructor. Then you can delegate the call via an explicit cast:
Foo(Foo& other) : Foo{const_cast<Foo const&>(other)} { }