Suppose you have a class called Product, defined like this:
class Product
{
public:
Product(const char *name, int i);
Product(Product &&rhs);
Product(const Product &rhs);
~Product();
private:
const char *m_name;
int m_i;
};
and you initialize a variable like this:
auto p = Product{"abc",123};
I thought that the standard dictated that a compiler must logically do the following:
construct a temporary Product
move-construct p (using the temporary Product)
But that the compiler was allowed to optimize this so that p is directly constructed.
I verified this (Visual Studio 2013) and indeed, the compiler optimizes this, even if we have our own custom (non-default) move-constructor. This is fine.
However, if I explicitly delete the copy- and move-constructor, like this:
class Product
{
public:
Product(const char *name, int i);
Product(Product &&rhs) = delete;
Product(const Product &rhs) = delete;
~Product();
private:
const char *m_name;
int m_i;
};
The auto+brace initialization still compiles. I though the compiler had to prevent this because there is no copy- or move- allowed.
Strange enough, if I make the deleted copy- and move-constructor private, like this:
class Product
{
public:
Product(const char *name, int i);
~Product();
private:
Product(Product &&rhs) = delete;
Product(const Product &rhs) = delete;
const char *m_name;
int m_i;
};
Then the auto+brace initialization doesn't compiler anymore.
error C2248: 'Product::Product' : cannot access private member declared in class 'Product'
Is this expected behavior?
Is this a bug in Visual Studio 2013 (Update 3)?
Note:
I tried compiling this on ideone and there it indeed refuses to compile the initialization when the copy- and move-constructors are deleted (and public). So I think this is a Visual Studio bug.
the standard is very clear as you mentioned before, indicating that this is a bug in the cl-compiler. You never can be sure, though if one compiler is saying something and all others disagree, I expect that this would be one of the many non-standard-compliant implementations of the MSVC compiler.
The interpretation of clang version 3.7 (svn-build):
t.cpp:19:7:{19:11-19:30}: error: call to deleted constructor of 'Product'
[Semantic Issue]
auto p = Product{"abc", 123};
^ ~~~~~~~~~~~~~~~~~~~
t.cpp:8:2: note: 'Product' has been explicitly marked deleted here
[Semantic Issue]
Product(Product &&rhs) = delete;
^
1 error generated.
make: *** [t.o] Error 1
The interpretation of gcc 4.8:
t.cpp: In function ‘int main()’:
t.cpp:19:29: error: use of deleted function ‘Product::Product(Product&&)’
auto p = Product{"abc", 123};
^
t.cpp:8:2: error: declared here
Product(Product &&rhs) = delete;
^
make: *** [build/gcc/t.o] Error 1
Keep also in mind that the Explicitly Defaulted and Deleted Functions are new since MSVC 2013 and the implementation of it is not yet complete. Like it does not yet understand =default for move constructors.
My guess would be that MSVC 2013 does not check for the move constructor or simply falls back to the copy constructor.
It might be interesting to check MSVC 2015, since it appears to have a (more) complete implementation of these constructions.
JVApen
In your line
auto p = Product{"abc",123};
the equal sign is not the denoting the assignment operator, but is just the syntax for an initializer. Hence, the compiler is not optimizing anything, but just doing the initialization.
Related
I updated my C++ toolchain from Visual Studio 2013 to Visual Studio 2017/2019.
Now I am experiencing a number of compile errors in the form:
<source>(13): error C2280: 'OfflineFixture::OfflineFixture(const OfflineFixture &)': attempting to reference a deleted function
<source>(8): note: compiler has generated 'OfflineFixture::OfflineFixture' here
<source>(8): note: 'OfflineFixture::OfflineFixture(const OfflineFixture &)': function was implicitly deleted because a data member invokes a deleted or inaccessible function 'std::unique_ptr<int,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)'
A unique pointer is a member of class that has no constructor and destructor.
In this case Visual Studio allows to instantiate an object this way:
OfflineFixture a{}; // works!
But using:
auto&& a = OfflineFixture{};
gives above compile error.
const auto& a = OfflineFixture{};
also gives above compile error.
Please have a look here:
https://gcc.godbolt.org/z/XtP40t
My question is: Is my code wrong?
The given examples compiles using:
gcc (9.1 and lower)
clang
Visual Studio 2013
But it fails in:
Visual Studio 2015
Visual Studio 2017
Visual Studio 2019
One way to fix this is to implement a default constructor in OfflineFixture.
Minimal example:
#include <memory>
struct OfflineFixture
{
void x() const {}
int i;
std::unique_ptr<int> m_calc;
};
int test() {
#if 1
const auto&& a = OfflineFixture{};
#else
OfflineFixture a{};
#endif
a.x();
return 0;
}
Let's make some preliminary points here.
In general, the statement:
const auto&& a = Foo{};
is perfectly legal in C++. Moreover, it is not true that it is undefined behavior. In fact, this is a perfect example of Reference Initialization with lifetime extension of a temporary.
Whenever a reference is bound to a temporary or to a subobject thereof, the lifetime of the temporary is extended to match the lifetime of the reference [...].
It continues with some exceptions (I won't quote all of them), but the declaration of a new object is not part of those exceptions.
Regarding your class, it appears to be clear the problem is the std::unique_ptr member.
Here a minimal example:
#include <memory>
struct OfflineFixture {
std::unique_ptr<int> m_calc;
};
void test() {
const auto&& a = OfflineFixture{}; // <--- Error in msvc
}
Somehow, MSVC tries to create a copy of the object. Indeed, the error is about invoking the copy constructor (which is deleted because of std::unique_ptr).
Since no copies should be performed here, it appears to be a msvc bug.
Note: gcc and clang compile fine with that.
I have added constructor and move constructor as shown in below code.
This resolves the issue.
struct OfflineFixture
{
void x() const {}
int i;
std::unique_ptr<int> m_calc;
OfflineFixture(){}
OfflineFixture(OfflineFixture &&){}
//implicit
OfflineFixture(const OfflineFixture&) = default;
OfflineFixture& operator=(const OfflineFixture&) = default;
~OfflineFixture() = default;
};
As, const auto&& a = OfflineFixture{}; requires constructor and move constructor
I hope it helps!
It is interesting to note that MSVC shows this erroneous behavior only if you do not provide a constructor or if you provide a default one:
struct OfflineFixture {
OfflineFixture()=default;
//...
};
The example can be fixed by providing a constructor with an empty block:
struct OfflineFixture {
OfflineFixture(){}
//...
};
which compiles just fine.
Consider the following toy program(prog.cpp):
class A {
public:
vector<int> vec;
A() noexcept {}
A(vector<int> s) : vec(s) {}
};
class B {
private:
vector<atomic<A>> a_table;
public:
B(int capacity) : a_table(capacity) {}
void update(int index) {
A newValue(vector<int>(10,1));
a_table[index].store(newValue);
}
};
int main(int argc, char** argv)
{
B b(5);
b.update(2);
return 0;
}
This when compiled normally (g++ prog.cpp -latomic), works fine. But when compiled as g++ -fsanitize=address -fno-omit-frame-pointer prog.cpp -latomic produces Double Free error when executed. A program based on similar lines as above has to be used in a multi-threaded application, where even the normal compilation is producing Double Free error. I read up Rule of Three/Five, which is generally referred to in case of Double Free, and various other documentations but nothing worked.
Also, removing noexcept specifier from the class A's default constructor produces this strange error, which also I would like to know about.
error: function ‘std::atomic<_Tp>::atomic() [with _Tp = A]’ defaulted on its first declaration with an exception-specification that differs from the implicit declaration ‘std::atomic<A>::atomic()’
atomic() noexcept = default;
^
std::atomic requires a trivially copyable type, which your A is not, because its member of type vector<int> (e.g.) is not trivially copy constructible.
GCC only detects a violation of that requirement since version 5.0.
The fact that older gcc versions compile the code does not mean that it is valid.
When attempting to delete the default constructor and provide a new one with a default parameter (and therefore still be able to default construct the object), I receive an ambiguity error from g++.
class Thing
{
public:
Thing() = delete;
Thing(int arg = 0) : arg(arg) {}
private:
int arg;
};
int main(int, char**)
{
Thing thing;
return 0;
}
Compilation error below:
$ g++ deletedConstructorTest.C -std=c++11
deletedConstructorTest.C: In function "int main(int, char**)":
deletedConstructorTest.C:12:9: error: call of overloaded "Thing()" is ambiguous
Thing thing;
^
deletedConstructorTest.C:12:9: note: candidates are:
deletedConstructorTest.C:5:5: note: Thing::Thing(int)
Thing(int arg = 0) :arg(arg) {}
^
deletedConstructorTest.C:4:5: note: Thing::Thing() <deleted>
Thing() = delete;
^
If I modify the example above by removing the "Thing() = delete;" line then it compiles fine. I understand that the compiler won't generate a default constructor since I provided my own so that line is unnecessary but I was surprised at the compilation error.
Thanks in advance!
All declared overloads are considered during overload resolution, even if they are deleted or inaccessible. So you've declared two default constructors, both of which are an equally good match for default-initialisation, hence the ambiguity.
In this case, simply don't declare the zero-argument default constructor at all. The one-argument constructor is usable as a default constructor, and declaring any constructor inhibits the generation of an implicit default constructor.
I've recently experienced some strange behaviour in my C++11 code.
I've got a class, which is only movable:
class only_move
{
public:
only_move() : value(0) {}
only_move(int value) : value(value) {}
only_move(const only_move&) = delete;
only_move& operator=(const only_move&) = delete;
only_move(only_move&&) = default;
only_move& operator=(only_move&&) = default;
int value;
};
And I've got another class, which contains an only_move object:
class has_only_move_member
{
public:
has_only_move_member() = delete;
has_only_move_member(only_move&& value) : member(std::move(value)) {}
only_move member;
};
If I understood it correct, that means has_only_move_member can't be copied, as the only_move member can't be copied. This means has_only_move_member(const has_only_move_member&) is deleted implicitly. Let's check that out:
has_only_move_member object(only_move(5));
has_only_move_member copy_of_object(object);
As expected, it prints out:
error: use of deleted function ‘has_only_move_member::has_only_move_member(const has_only_move_member&)’
note: ‘has_only_move_member::has_only_move_member(const has_only_move_member&)’ is implicitly deleted because the default definition would be ill-formed:
class has_only_move_member
Okay, so I moved on and put has_only_move_member instances in a std::map. As they don't have a copy constructor, I've moved them into it:
has_only_move_member object(only_move(5));
std::map<int, has_only_move_member> data;
data.emplace(5, std::move(object));
So far so good. This works like a charm.
But I had an idea. What about being more explicit and explicitly deleting the copy constructor of has_only_move_member. So I wrote into the class has_only_move_member:
has_only_move_member(const has_only_move_member&) = delete;
After that, the same code from above, there I moved objects into an map gives me an error:
/usr/include/c++/4.8/bits/stl_pair.h:134:45: error: use of deleted function ‘has_only_move_member::has_only_move_member(const has_only_move_member&)’
Yeah of course it's deleted, but why is there such a gap between implicit and explicit delete?
I'm getting the same behaviour with g++ 4.8.2 and clang 3.4-rc1 on Debian with libstdc++ and recent apple-clang-llvm 4.0 on Mac OS X 10.9 with libc++
I already said this in the comment but since this is a correct answer I am reposting as one:
You added a custom constructor (even though it's a deleted one). Therefore the compiler won't auto generate a move constructor. The compiler then falls back to copying which (as you requested) it obviously can't do.
I get compilation errors on g++ (GCC) 4.7.2 but not on MSVC-2012 when trying to std::vector::push_back a non-copyable (private copy constructor) but moveable object. To me my example looks identical to many other examples on SO and elsewhere. The error message makes it looks like a problem with the struct not being 'direct constructible' - I don't know what this means so am doubly unsure about why an object needs to be 'direct constructible' to be pushed back.
#include <vector>
#include <memory>
struct MyStruct
{
MyStruct(std::unique_ptr<int> p);
MyStruct(MyStruct&& other);
MyStruct& operator=(MyStruct&& other);
std::unique_ptr<int> mP;
private:
// Non-copyable
MyStruct(const MyStruct&);
MyStruct& operator=(const MyStruct& other);
};
int main()
{
MyStruct s(std::unique_ptr<int>(new int(5)));
std::vector<MyStruct> v;
auto other = std::move(s); // Test it is moveable
v.push_back(std::move(other)); // Fails to compile
return 0;
}
Gives errors
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/type_traits: In instantiation of ‘struct std::__is_direct_constructible_impl<MyStruct, const MyStruct&>’:
... snip ...
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/stl_vector.h:900:9: required from ‘void std::vector<_Tp, _Alloc>::push_back(std::vector<_Tp, _Alloc>::value_type&&) [with _Tp = MyStruct; _Alloc = std::allocator<MyStruct>; std::vector<_Tp, _Alloc>::value_type = MyStruct]’
main.cpp:27:33: required from here
main.cpp:16:5: error: ‘MyStruct::MyStruct(const MyStruct&)’ is private
Simple workaround from various answers:
Use MyStruct(const MyStruct&) = delete; instead of private ctor hack
Inherit boost::noncopyable (or another class with private ctor)
The failure is due to a limitation of G++ 4.7, which doesn't implement DR 1170, which was changed very late in the C++11 standardisation process to say that access checking should be done as part of template argument deduction.
The underlying cause is that libstdc++'s vector will move elements if the move operation is guaranteed not to throw (i.e. it's declared noexcept or throw()), otherwise if the type is copyable the elements will be copied, otherwise if the type is not copyable but does have a possibly-throwing move operation then it will be moved (and if an exception is thrown the results are undefined unspecified.) This is implemented with checks to the is_nothrow_move_constructible and is_copy_constructible type traits. In your case, the type is not nothrow move constructible, so the is_copy_constructible trait is checked. Your type has a copy constructor but it's not accessible, so the is_copy_constructible trait produces a compiler error with G++ 4.7 because access checking is not done during template argument deduction.
If you make your move constructor and move assignment operator noexcept then the type will be moved and doesn't need to be copyable, so the is_copy_constructible trait that fails is not used, and the code compiles OK.
Alternatively, (as also stated in the comments) if you make the copy constructor deleted then the is_copy_constructible trait gets the right result.
Another alternative is to use something like boost::noncopyable which implicitly makes the copy constructor deleted so the is_copy_constructible trait works properly (and also works with older compilers like MSVC that don't support deleted functions properly). I don't know what you mean about making it impossible to find the error, does MSVC not show you the full context of a compiler error?
Conclusion: use unique_ptr where appropriate but don't make classes explicitly movable
I disagree with this conclusion, it is too extreme. Instead make your classes nothrow movable whenever possible. Also, when possible, use deleted functions to make a type non-copyable instead of private+unimplemented functions, maybe using a macro for portability to older compilers e.g.
#if __cplusplus >= 201103L
#define NONCOPYABLE(TYPE) \
TYPE(const TYPE&) = delete; TYPE& operator=(const TYPE&) = delete
#else
// must be used in private access region
#define NONCOPYABLE(TYPE) \
TYPE(const TYPE&); TYPE& operator=(const TYPE&)
#endif
struct MyStruct
{
...
private:
NONCOPYABLE(MyStruct);
};