c++ - Explicitly delete never-use copy constructor give compile error -
i implementing sizetag
method take size value , keep l-value reference.
things work fine , in code intent use t&&
constructor.
however, if explicitly delete copy constructor compiler give error:
#include <cstdint> #include <type_traits> #include <utility> template <typename t = std::uint64_t> class sizetag { public: using size_type = std::uint64_t; using type = std::conditional_t<std::is_lvalue_reference<t>::value, const size_type&, size_type>; inline const type& get() const { return _size; } sizetag(t&& sz) : _size(std::forward<t>(sz)) { } sizetag& operator = (const sizetag&) = delete; sizetag(const sizetag&) = delete; // no error if line removed private: type _size; }; template <typename t> sizetag<t> make_size_tag(t&& t) { return std::forward<t>(t); } int main() { int = 9; make_size_tag(a); }
why happening? copy constructor should never called in case.
the function make_size_tag
returns sizetag<t>
value.
let's recap how function returning value works:
- there temporary object called return value object.
- for case
return expression;
:- the expression copy-initializes return value object.
- this copy elision context.
- for case
return { zero_or_more_items };
:- the braced list copy-list-initializes return value object.
- if calling code initializes variable function call, return value object initializer. (the exact form of initialization may vary depending on calling code). initialization of object, copy elision context.
in code, make_size_tag(a)
deduces t
(the parameter of make_size_tag
) int&
, because perfect forwarding scenario.
the instantiation of make_size_tag
t
looks like, after expanding out std::forward:
sizetag<int&> make_size_tag(int& t) { return t; }
because static_cast<int&>(t)
same t
, since t
lvalue of type int
.
as mentioned earlier, code copy-initializes return value object. code behaves sort of like:
sizetag<int&> temp_rv = t;
and because t
not sizetag
, definition of copy-initialization same as:
sizetag<int&> temp_rv = sizetag<int&>(t);
which invokes copy/move operation initialize temp_rv
temporary of type sizetag<int&>
. although copy elided copy elision, accessible copy/move constructor must exist.
the solution suggested jarod42, putting braces around return expression, works because equivalent initialization copy-list-initialization:
sizetag<int&> temp_list_rv { t };
which initializes temp_list_rv
using sizetag<int&>(int&)
constructor.
nb; code has separate bug: since type
const uint64_t &
, initialization of _size
int
creates temporary destroyed when sizetag
constructor completes; , tag returns dangling reference. clang warns this, g++ doesn't.
to fix this: either need change type
same t&
binds directly a
, e.g.:
using size_type = typename std::remove_reference<t>::type;
or make _size
not reference. seems latter defeat whole purpose of tag, might need rethink design bit.
to avoid possibility of generating danging reference, change const size_type &
size_type &
in conditional_t. compiler (assuming you're not using msvc) point out problem.