Why is a templatized copy constructor not called? [duplicate] - c++

This question already has answers here:
C++ template copy constructor on template class
(3 answers)
Closed 2 years ago.
Why is it that the following code fails to compile (complains about deleted copy constructor):
struct C {
int i;
C(const C&) = delete;
C(int i) : i(i) {}
template <typename T>
C(const T& value) {
cout << "in templatized constructor!\n";
this->i = value.i * 2;
}
};
int main()
{
C s1{4};
C s2{s1};
cout << s2.i;
}
But when i remove the const from the templatized copy constructor, everything works:
template <typename T>
C(T& value) {
cout << "in templatized constructor!\n";
this->i = value.i * 2;
}
Two questions:
(1) Why isn't the template contructor used as a fall-back when the generated copy constructor is deleted?
(2) Even though "templates can't be copy constructors" - how/why is it that the C(T&); constructor IS being used as a copy constructor (in the second example) ? surely this contradicts the idea that templates can't be copy constructors?

It's just overload resolution. In C s2{s1};, you are looking for a constructor of C that can take a (non-const) C lvalue argument. For template<typename T> C(T const&), the "closest" this constructor can get to matching the argument list is setting T = C, so you get C(C const&) (this solution for T is found by template argument deduction, which we don't need to go into). The copy constructor C(C const&) = delete; can also be called with this argument list. The two implicit conversion sequences involved (C lvalue undergoes identity conversion to bind to C const&), are also exactly the same, so neither overload is immediately "better" than the other than the other. However, the deleted one is not a template, so it's considered to be better anyway. The deleted overload is chosen, causing an error. If you have instead template<typename T> C(T&), then it can get closer to the given argument list, by setting T = C to get C(C&). Now, the implicit conversion sequence where a C lvalue binds to a C& (the case of the template) is better than the implicit conversion sequence where a C lvalue binds to a C const& (the copy constructor), since there are fewer qualifiers to add, so now the template wins and the code compiles.
Addressing the comments, your templated constructor indeed isn't a copy constructor. According to cppreference,
A copy constructor of class T is a non-template constructor...
Also, you may be misunderstanding what = delete; means. C(C const&) = delete; does not mean "there is not an overload with this signature", it means "there is an overload with this signature, and trying to call it is an error". You can't not have a copy constructor as one of the overloads of a class's constructor. deleteing it only makes it an error to use that overload but doesn't stop overload resolution from picking it over another overload.

Related

What can be done to prevent misleading assigment to returned value?

After many years of using C++ I realized a quirk in the syntax when using custom classes.
Despite being the correct language behavior it allows to create very misleading interfaces.
Example here:
class complex_arg {
double r_;
double phi_;
public:
std::complex<double> value() const {return r_*exp(phi_*std::complex<double>{0, 1});}
};
int main() {
complex_arg ca;
ca.value() = std::complex<double>(1000., 0.); // accepted by the compiler !?
assert( ca.value() != std::complex<double>(1000., 0.) ); // what !?
}
https://godbolt.org/z/Y5Pcjsc8d
What can be done to the class definition to prevent this behavior?
(Or at the least flag the user of the clas that the 3rd line is not really doing any assignment.)
I see only one way out, but it requires modifying the class and it doesn't scale well (to large classes that can be moved).
const std::complex<double> value() const;
I also tried [[nodiscard]] value() but it didn't help.
As a last resort, maybe something can be done to the returned type std::complex<double> ? (that is, assuming one is in control of that class)
Note that I understand that sometimes one might need to do (optimized) assign to a newly obtained value and passe it to yet another function f( ca.value() = bla ). I am not questioning this usage per se (although it is quite confusing as well); I have the problem mostly with ca.value() = bla; as a standalone statement that doesn't do what it looks.
Ordinarily we can call a member function on an object regardless of whether that object's value category is an lvalue or rvalue.
What can be done to the class definition to prevent this behavior?
Prior to modern C++ there was no way prevent this usage. But since C++11 we can ref-qualify a member function to do what you ask as shown below.
From member functions:
During overload resolution, non-static member function with a cv-qualifier sequence of class X is treated as follows:
no ref-qualifier: the implicit object parameter has type lvalue reference to cv-qualified X and is additionally allowed to bind rvalue implied object argument
lvalue ref-qualifier: the implicit object parameter has type lvalue reference to cv-qualified X
rvalue ref-qualifier: the implicit object parameter has type rvalue reference to cv-qualified X
This allows us to do what you ask for a custom managed class. In particular, we can & qualify the copy assignment operator.
struct C
{
C(int)
{
std::cout<<"converting ctor called"<<std::endl;
}
C(){
std::cout<<"default ctor called"<<std::endl;
}
C(const C&)
{
std::cout<<"copy ctor called"<<std::endl;
}
//-------------------------v------>added this ref-qualifier
C& operator=(const C&) &
{
std::cout<<"copy assignment operator called"<<std::endl;;
return *this;
}
};
C func()
{
C temp;
return temp;
}
int main()
{
//---------v---------> won't work because assignment operator is & qualified
func() = 4;
}

class constructor precedence with a variadic template constructor for a value wrapper

Today I've discovered that I don't understand the C++ constructor precedence rules.
Please, see the following template struct wrapper
template <typename T>
struct wrapper
{
T value;
wrapper (T const & v0) : value{v0}
{ std::cout << "value copy constructor" << std::endl; }
wrapper (T && v0) : value{std::move(v0)}
{ std::cout << "value move constructor" << std::endl; }
template <typename ... As>
wrapper (As && ... as) : value(std::forward<As>(as)...)
{ std::cout << "emplace constructor" << std::endl; }
wrapper (wrapper const & w0) : value{w0.value}
{ std::cout << "copy constructor" << std::endl; }
wrapper (wrapper && w0) : value{std::move(w0.value)}
{ std::cout << "move constructor" << std::endl; }
};
It's a simple template value wrapper with copy constructor (wrapper const &), a move constructor (wrapper && w0), a sort of value copy constructor (T const & v0), a sort of move constructor (T && v0) and a sort of template construct-in-place-the-value constructor (As && ... as, following the example of emplace methods for STL containers).
My intention was to use the copy or moving constructor calling with a wrapper, the value copy or move constructor passing a T object and the template emplace constructor calling with a list of values able to construct an object of type T.
But I don't obtain what I expected.
From the following code
std::string s0 {"a"};
wrapper<std::string> w0{s0}; // emplace constructor (?)
wrapper<std::string> w1{std::move(s0)}; // value move constructor
wrapper<std::string> w2{1u, 'b'}; // emplace constructor
//wrapper<std::string> w3{w0}; // compilation error (?)
wrapper<std::string> w4{std::move(w0)}; // move constructor
The w1, w2 and w4 values are constructed with value move constructor, emplace constructor and move constructor (respectively) as expected.
But w0 is constructed with emplace constructor (I was expecting value copy constructor) and w3 isn't constructed at all (compilation error) because the emplace constructor is preferred but ins't a std::string constructor that accept a wrapper<std::string> value.
First question: what am I doing wrong?
I suppose that the w0 problem it's because s0 isn't a const value, so the T const & isn't an exact match.
Indeed, if I write
std::string const s1 {"a"};
wrapper<std::string> w0{s1};
I get the value copy constructor called
Second question: what I have to do to obtain what I want?
So what I have to do to make the value copy constructor (T const &) to get the precedence over the emplace constructor (As && ...) also with not constant T values and, mostly, what I have to do to get the copy constructor (wrapper const &) take the precedence constructing w3?
There is no such thing as "constructor precedence rules," there's nothing special about constructors in terms of precedence.
The two problem cases have the same underlying rule explaining them:
wrapper<std::string> w0{s0}; // emplace constructor (?)
wrapper<std::string> w3{w0}; // compilation error (?)
For w0, we have two candidates: the value copy constructor (which takes a std::string const&) and the emplace constructor (which takes a std::string&). The latter is a better match because its reference is less cv-qualified than the value copy constructor's reference (specifically [over.ics.rank]/3). A shorter version of this is:
template <typename T> void foo(T&&); // #1
void foo(int const&); // #2
int i;
foo(i); // calls #1
Similarly, for w3, we have two candidates: the emplace constructor (which takes a wrapper&) and the copy constructor (which takes a wrapper const&). Again, because of the same rule, the emplace constructor is preferred. This leads to a compile error because value isn't actually constructible from a wrapper<std::string>.
This is why you have to be careful with forwarding references and constrain your function templates! This is Item 26 ("Avoid overloading on universal references") and Item 27 ("Familiarize yourself with alternatives to overloading on universal references") in Effective Modern C++. Bare minimum would be:
template <typename... As,
std::enable_if_t<std::is_constructible<T, As...>::value, int> = 0>
wrapper(As&&...);
This allows w3 because now there is only one candidate. The fact that w0 emplaces instead of copies shouldn't matter, the end result is the same. Really, the value copy constructor doesn't really accomplish anything anyway - you should just remove it.
I would recommend this set of constructors:
wrapper() = default;
wrapper(wrapper const&) = default;
wrapper(wrapper&&) = default;
// if you really want emplace, this way
template <typename A=T, typename... Args,
std::enable_if_t<
std::is_constructible<T, A, As...>::value &&
!std::is_same<std::decay_t<A>, wrapper>::value
, int> = 0>
wrapper(A&& a0, Args&&... args)
: value(std::forward<A>(a0), std::forward<Args>(args)...)
{ }
// otherwise, just take the sink
wrapper(T v)
: value(std::move(v))
{ }
That gets the job done with minimal fuss and confusion. Note that the emplace and sink constructors are mutually exclusive, use exactly one of them.
As OP suggested, putting my comment as an answer with some elaboration.
Due to the way overload resolution is performed and types are matched, a variadic forward-reference type of constructor will often be selected as a best match. This would happen because all const qualification will be resolved correctly and form a perfect match - for example, when binding a const reference to a non-const lvalue and such - like in your example.
One way to deal with them would be to disable (through various sfinae methods at our disposal) variadic constructor when argument list matches (albeit imperfectly) to any of other available constructors, but this is very tedious, and requires ongoing support whenever extra constructors are added.
I personally prefer a tag-based approach and use a tag type as a first argument to variadic constructor. While any tag structure would work, I tend to (lazily) steal a type from C++17 - std::in_place. The code now becomes:
template<class... ARGS>
Constructor(std::in_place_t, ARGS&&... args)
And than called as
Constructor ctr(std::in_place, /* arguments */);
Since in my experience in the calling place the nature of constructor is always known - i.e. you will always know if you intend to call forward-reference accepting constructor or not - this solution works well for me.
As said in the comment, the problem is that the variadic template constructor
takes argument by forwarding reference, so it is a better match for non const lvalue copy or const rvalue copy.
There are many way to disable it, one efficient way is to always use a tag as in_place_t as proposed by SergeyA in its answer. The other is to disable the template constructor when it matches the signature of a copy constructor as it is proposed in the famous Effective C++ books.
In this case, I prefer to declare all possible signature for copy/move constructors (and also copy/move assignment). This way, whatever new constructor I add to the class, I will not have to think about avoiding copy construction, it is short 2 line of code, easy to read and it does not pollute the interface of other constructors:
template <typename T>
struct wrapper
{
//...
wrapper (wrapper& w0) : wrapper(as_const(w0)){}
wrapper (const wrapper && w0) : wrapper(w0){}
};
NB: this solution should not be used if one plan to use it as a volatile type, or if all the following condition are fullfilled:
the object size is smaller than 16bytes (or 8 byte for MSVC ABI),
all member suboject are trivially copyable,
this wrapper is going to be passed to functions where special care is taken for the case where the argument is of a trivially copyable type and its size is lower than the previous threshold because in this case, the argument can be passed in a register (or two) by passing the argument by value!
If all these requirement are fulfilled, then you may think about implementing less maintainable (error prone -> next time one will modify the code) or client interface polluting solution!

Why isn't the most appropriate constructor called in this case?

Consider the following class:
class foo {
int data;
public:
template <typename T, typename = enable_if_t<is_constructible<int, T>::value>>
foo(const T& i) : data{ i } { cout << "Value copy ctor" << endl; }
template <typename T, typename = enable_if_t<is_constructible<int, T>::value>>
foo(T&& i) : data{ i } { cout << "Value move ctor" << endl; }
foo(const foo& other) : data{ other.data } { cout << "Copy ctor" << endl; }
foo(foo&& other) : data{ other.data } { cout << "Move ctor" << endl; }
operator int() { cout << "Operator int()" << endl; return data; }
};
Of course it doesn't make much sense to take a single int by any kind of reference, but this is just an example. The data member could be very expensive to copy, hence all the move semantics.
That fancy template basically enables any type from which data can be constructed. So a foo object can be constructed either by copying or moving a value of any type that satisfies this criteria, or simply by copying or moving another object of type foo. Pretty straight forward so far.
The problem occurs when you try to do something like this:
foo obj1(42);
foo obj2(obj1);
What this should do (at lest in my opinion) is to construct the first object by moving the value 42 into it (since it's an rvalue), and then construct the second object by copying the first object. So what this should print out is:
Value move ctor
Copy ctor
But what it actually prints out is:
Value move ctor
Operator int
Value move ctor
The first object gets constructed just fine, no problem there. But instead of calling the copy constructor to construct the second object, the program converts the first object into another type (via the conversion we defined) and then calls another constructor of foo which can move from that type (since it's an rvalue at that point).
I find this very strange, and it's definitely not the behavior I would want from this piece of code. I think it makes more sense to just call the copy constructor upon constructing the second object, as that seems way more trivial given the type of argument I supplied.
Can anyone explain what happens here? Of course I understand that since there's a user-defined conversion to int, this is a perfectly valid path to take, but I cannot make sense of it. Why would the compiler refuse to simply call the constructor which has the exact same argument type as the supplied value? Wouldn't that be the most trivial thing to do, therefore the default behavior? Calling the conversion operator does perform a copy as well, so I don't think that is faster or more optimal than simply calling the copy constructor either.
Your template "move" constructor (with T = foo &) has a parameter of type foo &, which is a better match than your copy constructor, since that only takes const foo &. Your template constructor then fills data by converting i to int, invoking operator int().
The simplest immediate fix could be to use enable_if to restrict your move constructor to move operations: if T is deduced as an lvalue reference type (meaning your T&& i would collapse to an lvalue reference too), force a substitution failure.
template <typename T, typename = enable_if_t<is_constructible<int, T>::value>,
typename = enable_if_t<!is_lvalue_reference<T>::value>>
foo(T&& i) : data( std::move(i) ) { cout << "Value move ctor" << endl; }
Note: since your parameter has a name, inside the constructor, just like all other named objects, it's an lvalue. Given that you want to move from it, you can use std::move.
More generally, you could use perfect forwarding (accepting both lvalues and rvalues), and only remove foo itself as a special exception:
template <typename T, typename = enable_if_t<is_constructible<int, T>::value>
, typename = enable_if_t<!is_same<decay_t<T>, foo>::value>
foo(T&& i) : data( std::forward<T>(i) ) { cout << "Forwarding ctor" << endl; }
This would replace your value copy and value move constructor.
Another note: is_constructible<int, T>::value is a test that tells you whether data(std::forward<T>(i)) would be well-formed. It does not test whether data{std::forward<T>(i)} would be well-formed. A T for which the result is different is long, since the long to int conversion is a narrowing conversion, and narrowing conversions are not allowed in {}.
It happens because of operator int().
because of operator int(), obj1 calls operator int() and casts itself as an int.
Possible solutions:
use static_cast<foo>. this will cast the variable that normally should cast as an int, to a foo.
Anyone please edit this question and fill this. I have no more ideas.

std::tuple with generic types like boost::any

Dear fellow programmers,
the code below gives me some headaches. It tries to add a 'generic' object (=object that can be constructed from anything) to a tuple and then copy that tuple.
#include <tuple>
#include <iostream>
#include <typeinfo>
struct anything
{
anything()
{}
anything(const anything&)
{
std::cout << "Called copy c'tor" << std::endl;
}
template<class T>
anything(T arg)
{
std::cout << "Called c'tor with with argument of type " << typeid(arg).name() << std::endl;
// new T(arg); // causes stack overflow
}
};
int main()
{
std::tuple<anything> t;
//
std::cout << "Copy constructing t2, expecting copy c'tor to be called." << std::endl;
std::tuple<anything> t2(t);
return 0;
}
With VS 2015 Update 2 it doesn't even compile, the line
std::tuple<anything> t2(t);
triggers a compiler error deep in tuple.h.
With gcc 5.3.1 it compiles, but the output is not what I'd expect:
Copy constructing t2, expecting copy c'tor to be called.
Called copy c'tor
Called c'tor with with argument of type St5tupleIJ8anythingEE
What I don't understand is the last line, i.e. why the templated constructor gets called with std::tuple as argument?
This is actually a real world problem. In my application I use a boost::signal of signature
typedef boost::type_erasure::any
<boost::mpl::vector<
boost::type_erasure::typeid_<>,
boost::type_erasure::copy_constructible<>,
boost::type_erasure::less_than_comparable<>,
boost::type_erasure::equality_comparable<>,
boost::type_erasure::relaxed
>> KeyType;
boost::signals2::signal<void(const KeyType&)>
Boost signals internally wraps the argument in a tuple before calling the slot function with it, which eventually results in a stack overflow, because the tuple c'tor calls the any c'tor with tuple as argument and the any c'tor then calls the tuple c'tor with 'any of tuple' and so on and on and on...
Let's go through overload resolution for:
std::tuple<anything> t2(t);
We have three viable constructors at our disposal:
explicit tuple( const Types&... args ); // (2) with Types = [anything]
template< class... UTypes >
explicit tuple( UTypes&&... args ); // (3) with UTypes = [std::tuple<anything>&]
tuple( const tuple& other ) = default; // (8)
Which have these argument lists:
tuple(const anything& ); // (2)
tuple(std::tuple<anything>& ); // (3)
tuple(std::tuple<anything> const& ); // (8)
(2) involves a user-defined conversion whereas (3) and (8) are exact matches. When it comes to reference bindings:
Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence
S2 if [...] S1 and S2 are reference bindings (8.5.3), and the types to which the references refer are the same
type except for top-level cv-qualifiers, and the type to which the reference initialized by S2 refers
is more cv-qualified than the type to which the reference initialized by S1 refers.
So (3) is preferred - since it's less cv-qualified than (8). That constructor calls anything(std::tuple<anything> ).
As far as solutions, what you need is for (3) to not be considered in this case - we need to make it not a viable option (since (8) is already preferred to (2)). Currently, the easiest thing is just to make your constructor explicit:
template<class T>
explicit anything(T arg) { ... }
this works since (3) is specified in terms of is_convertible<>, which will return false on explicit conversions. However, that's currently considered a defect and will likely be changed in the future - since after all, we are explicitly constructing every aspect here so the explicit constructors should still be considered!
Once that happens, you're kind of out of luck as far as direct copy construction goes. You'd have to do something like disable your anything constructor for tuple? That seems... not great. But in that case, marking that constructor explicit would still work for copy-initialization:
std::tuple<anything> t2 = t;
which works now even without marking the anything constructor explicit, due to the same aforementioned defect.
If you look at the implementation of the tuple you'll notice that
_Tuple_val<_This> _Myfirst; // the stored element
...
template<class _This2,
class... _Rest2,
class = typename _Tuple_enable<tuple<_This2, _Rest2...>, _Myt>::type>
explicit tuple(_This2&& _This_arg, _Rest2&&... _Rest_arg)
: _Mybase(_STD forward<_Rest2>(_Rest_arg)...),
_Myfirst(_STD forward<_This2>(_This_arg))
{ // construct from one or more moved elements
}
The constructor of the tuple passes first argument to the the constructor of the first element of tuple. As variable t has type std::tuple<anything> compiler finds the best match for constructing anything with t - a template constructor.
To copy a tuple you simply need to write
std::tuple<anything> t2 = t;
UPD.
According to standard std::tuple provides following constructors:
template <class... UTypes>
explicit constexpr tuple(const Types&...);
template <class... UTypes>
constexpr tuple(const tuple<UTypes...>&);
And apparently your template constructor is a better match than copy constructor of the tuple.

About conversion constructor and assignment operator

How it comes that operation like foo = int can be done by both foo(int) (conversion constructor) and foo::operator=(int) (overloaded assignment operator)? When one be called instead of other (maybe one is rudimentary)?
#include <iostream>
class foo
{
public:
foo(){}
foo(int r_value)
{
std::cout << "\nfoo::foo(int)\n";
}
void operator=(int r_value)
{
std::cout << "\nfoo::operator=(int)\n";
}
};
int main()
{
foo f;
f = 57;
return 0;
}
Code above makes operator=(int) to run when both exist and foo(int) if operator=(int) is commented (or opposite).
This is basic overload resolution. Both overloads are viable:
Binding 57 to foo::operator=(int) (exact match)
Implicitly converting 57 to foo via the converting constructor, and binding the temporary foo object to the implicitly defined foo::operator=(foo const &).
Since the latter requires more conversions than the former, it is a less-good match, and the former overload is chosen.
You can still achieve the second call by making it explicit:
f = foo(57); // binds perfectly to foo::operator=(foo const &)
The full set of rules for overload resolution are rather long and involved, but in individual cases like this the answer is straight-forward. See 13.3 ([over.match]) for the full, gory details, though.
There is a difference:
foo a = 10;
Calls foo::foo(int)
foo a;
a = 10;
Calls foo::operator=(int) in a
Both of the implementations are different. The first is a Constructor and the second is an assignment. The Use cases varies, and each of them would be called accordingly based on the use case.
Use Case
The Constructor is called foo::foo(int)
foo f = 57;
The Assignment is called foo::operator=(int)
foo f;
f = 57;
Note
Using assignment in the above use case and in your example has more overhead, as it called the default constructor along with the assignment.
For this statement
f = 57;
the compiler at first considers all functions operator =. There are two such functions: the explicitly defined by you and the copy assignment operator implicitly defined by the compiler. The first one is the best suitable function. So it is called.
If you will comment this assignment operator then the compiler has only one function operator =. It is the implicitly defined copy assignment operator. But it can not be applied directly. So the compiler seeks a way to convert supplied argument to type foo. And it can do this by calling the conversion construuctor.