Compiler generated move constructor behavior in VC12 (VS2013) [duplicate] - c++

This question already has an answer here:
Default Move Constructor in Visual Studio 2013 (Update 3)
(1 answer)
Closed 7 years ago.
I was trying to test the behavior of compiler generated Move Constructor for a class having data members as "std::string" and "std::shared_ptr".
class Record
{
public:
Record():mData("defRec"), mDataPtr(new std::string("defRec"))
{}
Record(const std::string &str): mData(str), mDataPtr(new std::string(str))
{}
private:
std::string mData;
std::shared_ptr<std::string> mDataPtr;
};
Any D'tor, move and copy operations have been deliberately omitted to allow compiler to generate Move operations. The class data members have especially been chosen since unlike the Record class, they both have D'tor, move and copy operations defined.
After the following code,
Record rec1(std::string("Record1"));
Record rec2(std::move(rec1));
on inspecting object rec1 (via debugger or by defining access methods), mData still contains "Record1" and mDataPtr still holds the string pointer.
However, If I provide my own Move Constructor as below
Record(Record&& rhs) : mData(std::move(rhs.mData)), mDataPtr(std::move(rhs.mDataPtr))
{}
then after the following code
Record rec1(std::string("Record1"));
Record rec2(std::move(rec1));
on inspecting object rec1, both mData and mDataPtr become empty object.
I don't understand why the compiler generated move constructor for "class Record" gives different result to my version of move constructor for "class Record" where I just ensure the move C'tor for data members to be invoked. I would expect the same behavior for compiler generated move constructor?
Though the moved from object is supposed to be in a valid state, the data members' move constructor (not defined by me on both occasions) gives different results.
Is this a known limitation of VC12 (VS2013)?

Yes this is a known limitation of that compiler. From Can a move constructor be implicit? the question's EDIT EDIT shows us:
EDIT EDIT I pinged Stephan T Lavavej and asked him why VC 2012 doesn't
seem to follow what draft 12.8 states regarding implicit move
constructor generation. He was kind enough to explain:
It's more of a "feature not yet implemented" than a bug. VC currently implements what I refer to as rvalue references v2.0, where
move ctors/assigns are never implicitly generated and never affect the
implicit generation of copy ctors/assigns. C++11 specifies rvalue
references v3.0, which are the rules you're looking at.
In short the move constructor is never being generated by the compiler and the copy constructor is called instead even in the presence of the explicit std::move.
According to the standard, there are a bunch of reasons the compiler wouldn't be required to generate a default move constructor but your class doesn't appear to violate any of them.

Related

Paradox with default c'tor potentially throwing for a class with a std::string member

Do the C++ "Core Guidelines" prevent (or, at least, strongly discourage) a class with a std::string member from having a default constructor? I ask because of an issue I've noticed when using the MSVC Code Analysis tool (in Visual Studio 2019) in its most 'severe' setting; the issue is the "C26455" warning, as discussed in this document:
The C++ Core Guidelines suggest that default constructors shouldn't do
anything that can throw. If the default constructor is allowed to
throw, operations such as move and swap will also throw which is
undesirable because move and swap should always succeed. Parameterized
constructors may throw.
The problem is illustrated in the following, trivial code sample (when compiled using the C++17 Standard – see the "edit" note, below):
#include <string>
class Person {
private:
std::string my_name;
public:
// C26455: Default constructor may not throw. Declare it 'noexcept'...
Person() : my_name{ "Anonymous" } {}
// // C26447: The function is declared 'noexcept' but calls function 'allocator<char> >()' which may throw exceptions...
// Person() noexcept : my_name{ "Anonymous" } {}
};
Now, if I heed the generated C26455 warning (as shown in the comment), and add the noexcept specifier to the default constructor (that commented-out in the code sample above), I get a different warning, indicating that my constructor calls a routine in the standard library that may throw:
warning C26447: The function is declared 'noexcept' but calls function
'allocator >()' which may throw exceptions (f.6).
Note that I get the same warning if I remove the initializer list from the constructor and add that default value initializer to the declaration of the my_name member (as std::string my_name{ "Anonymous" };). However, all warnings disappear if I don't explicitly initialize the my_name member – which means I rely on the default constructor for std::string to create an empty object of unspecified capacity.
Presumably, another option would be to provide my own, non-throwing allocator, and pass that as the second argument to the my_name constructor – but this seems, to me, to be a somewhat over-complex solution to what must be a very common use case. (Similar situations would arise when using other standard library container objects as class members that require initialization involving allocation.)
Is there a simple resolution to this issue? Or should I just ignore/disable those code analysis warnings and core guidelines?
EDIT: Note that, when compiling to the C++20 Standard, the C26447 warning goes away when adding the noexcept specifier to the default constructor. I don't understand what C++20 offers that makes this happen; if I did, then maybe that would give a hint as to how to handle the situation for code compiled using the C++17 Standard (as I would prefer).
In my earlier (now deleted) answer, I speculated that it was that the string literal operator (""s) has a constexpr version from C++20 onwards, but it appears that this cannot, in itself, provide the explanation.

What is the difference between setting the copy constructor to private and =delete? [duplicate]

This question already has answers here:
delete modifier vs declaring function as private
(4 answers)
Closed 3 years ago.
I have seen a lot of books recommend using =delete, is this just clear what it means? (making the program more readable) rather than saying that it is a bad thing to set the copy constructor to private?
Thinks your answers
class A {
A(const A&);
// some functions and variable
public:
// or you can A(const A&)=delete;
// do something
};
This is a relatively new functionality (added in the 2011 revision of C++) whose main motivation surely was readability and clarity of intent. However, the difference is more than just cosmetic.
Remember that with a constructor declared in the class, nothing prevents some other translation unit from actually providing the definition. It is quite usual to just list a class' member functions in a header file and implement them in a separate .cpp. If someone uses the copy constructor from inside the class, the compiler will complain that the definition is missing ("undefined reference to..."). If a naive programmer somehow reaches the conclusion that you forgot to implement it because you never needed it, they can go ahead and do so. Suddenly your class is copyable, even though only from within its own member functions (and friends). The =delete constructor prevents this, and the compiler errors are nicer (usually along the lines of "the object can't be copied because the copy constructor was declared as deleted" rather than "undefined reference to ..." or "A::A is private within this context").

Does C++ 14 still generate default functions even if the class contains no data?

I am rereading Scott Meyers’ Effective C++ after a hiatus of 16 years. Although I have not read the latest C++ standard, but it has come to my attention that C++ has changed since the second edition of Effective C++ was written.
In the third edition of his book, Scott Meyers has mentioned that even if you have an empty class, meaning there is nothing to initialize or assign, a C++ compiler will still generate at least 3 default functions, namely, default constructor, default copy constructor, assignment operator, and probably some other functions. According to Mr. Meyers, the following code will result in the generation of the aforementioned functions.
class Empty {}
Empty E1; // Default constructor.
Empty E2 ( E1 ); // Default copy constructor.
E1 = E2; // Default assignment operator.
Considering that there really is nothing to initialize, as the class is empty, does the C++ still generate some sort of code for the stated functions?
probably some other functions.
Yes, the move constructor and the move assignment operator. That's it.
Considering that there really is nothing to initialize, as the class is empty, does the C++ still generate some sort of code for the stated functions?
Sometimes. What happens is that those special members are declared, but not defined. They are only defined when they are used (i.e. odr-used or constant evaluated), otherwise, there is no code generated for them as they aren't defined.
And because you have an empty class, if the special members are defined, they will do nothing at all.

Are copy constructors defined implicitly always, or only when they are used?

Consider the following code:
#include <memory>
#include <vector>
class A
{
private:
std::vector<std::unique_ptr<int>> _vals;
};
int main()
{
A a;
//A a2(a);
return 0;
}
Compiler A compiles this without issue unless I uncomment out the line A a2(a); at which point it complains about the copy constructor for std::unique_ptr being deleted, and therefore I can't copy construct A. Compiler B, however, makes that complaint even if I leave that line commented out. That is, compiler A only generates an implicitly defined copy constructor when I actually try to use it, whereas compiler B does so unconditionally. Which one is correct? Note that if I were to have used std::unique_ptr<int> _vals; instead of std::vector<std::unique_ptr<int>> _vals; both compilers correctly implicitly delete both copy constructor and assignment operator (std::unique_ptr has a explicitly deleted copy constructor, while std::vector does not).
(Note: Getting the code to compile in compiler B is easy enough - just explicitly delete the copy constructor and assignment operator, and it works correctly. That isn't the point of the question; it is to understand the correct behavior.)
From [class.copy.ctor]/12:
A copy/move constructor that is defaulted and not defined as deleted is implicitly defined when it is odr-used ([basic.def.odr]), when it is needed for constant evaluation ([expr.const]), or when it is explicitly defaulted after its first declaration.
A's copy constructor is defaulted, so it's implicitly defined only when it is odr-used. A a2(a); is just such an odr-use - so it's that statement that would trigger its definition, that would make the program ill-formed. Until the copy constructor is odr-used, it should not be defined.
Compiler B is wrong to reject the program.
Note: My answer is based on your comment:
[...] it's only on Windows, and only when I explicitly list class A as a DLL export (via, e.g., class __declspec(dllexport) A) that this happens. [...]
On MSDN we can learn that declaring a class dllexport makes all members exported and required a definition for all of them. I suspect the compiler generates the definitions for all non-deleted functions in order to comply with this rule.
As you can read here, std::is_copy_constructible<std::vector<std::unique_ptr<int>>>::value is actually true and I would expect the supposed mechanism (that defines the copy constructor in your case for export purposes) checks the value of this trait (or uses a similar mechanism) instead of actually checking whether it would compile. That would explain why the bahviour is correct when you use unique_ptr<T> instead of vector<unique_ptr<T>>.
The issue is thus, that std::vector actually defines the copy constructor even when it wouldn't compile.
Imho, a is_copy_constructible check is sufficient because at the point where your dllexport happens you cannot know whether the implicit function will be odr-used at the place where you use dllimport (possibly even another project). Thus, I wouldn't think of it as a bug in compiler B.
While I can't confirm this behavior (I do not have access to Windows compiler, and OP claims the bug happens with icc on Windows platform), taking the question at it's face value, the answer is - compiler B has a gross bug.
In particular, implicitly-declared copy constructor is defined as deleted, when ...
T has non-static data members that cannot be copied (have deleted,
inaccessible, or ambiguous copy constructors);
https://en.cppreference.com/w/cpp/language/copy_constructor
Thus, conforming compiler must semantically generate deleted copy-constructor, and successfully compile the program since such constructor never called.

C++ result of = default methods

Is there a way to actually see (output to file, console etc) implicit compiler generated or = default methods (in C++)?
(Target compilers: vc, clang, gcc)
I'd like to see how these functions are actually implemented. For example, how does the assignment operator assigns its values, does it check for self assignment, is const correctness given etc). I did not found any statisfying result on the www that actually shows the implementations of these compiler generated functions.
The semantics of the "special member" functions are defined by the C++ standard, in
section 12
In brief, they do what you think they do;
The default will invoke the defaults of the bases and the members
The copy will invoke the copy of the bases and members
The same applies for the moves, assignments and destructor
If, for some reason, one of them would violate a const correctness or the corresponding constructor or assignment of the base or members is not available, or some other issue arrises, then that special member is not declared.
An an additional note; there are specific rules that apply to the defaults if one (or some) of the special members are defined by the user (detailed here).