Why is private move constructor allowed while initalizing via static method? - gcc

Simplified code snippet is:
class A {
public:
~A();
static A create();
private:
A() = default;
A(A&&) = default;
NonCopyable n;
};
A A::create() {
A a;
return a;
}
int main(int argc, char* argv[]) {
auto a = A::create();
return 0;
}
Please also see my live example (which shows different compilers' behavior).
In the end, I'm wondering why does auto a = A::create(); compile without errors using newer compilers [gcc >= 7.1] (which part of the C++17 standard is relevant here?), given that:
We have a non-copyable member NonCopyable n;, so default copy constructor would be ill-formed.
It's an NRVO here since A a; return a; so copy elision is not guaranteed by the standard.
Move constructor A(A&&) is marked private.
Optimizations were off -O0 for testing.
My suspicion is that move constructor is being "validated" by the compiler at return a;; since this is a member function of A it passes the validation. Even if the suspicion is correct, I'm not sure if this is standard-compliant.

I believe this is a consequence of P0135: Wording for guaranteed copy elision through simplified value categories, specifically the change to [dcl.init]:
If the initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the class of the destination, the initializer expression is used to initialize the destination object.
[Example: T x = T(T(T())); calls the T default constructor to initialize x. — end example]
As a result, this behavior is not dependent on copy elision of return values or the availability of move constructors.

Related

Why auto initialization differs between C++11 and C++17?

The following code compiles in C++17 and does not compile in C++11
class Value
{
public:
Value() = default;
~Value() = default;
Value(const Value&) = default;
Value& operator=(const Value&) = default;
Value(Value&&) = delete;
Value& operator=(Value&&) = delete;
};
int main()
{
auto value = Value{};
}
The message is:
error: call to deleted constructor of 'Value'
auto value = Value{};
^ ~~~~~~~
note: 'Value' has been explicitly marked deleted here
Value(Value&&) = delete;
But if I change to:
int main()
{
Value value{};
}
Then it is fine for both C++11 and C++17.
Does this have anything to copy elision introduced in C++17?
"Copy elision" itself was not introduced in C++17, it was already present in prior versions of the standard. What allows your code snippet to be compiled in C++17 are the changes in section "11.6 Initializers" (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4713.pdf):
If the initializer expression is a prvalue and the cv-unqualified version of the source type is the same
class as the class of the destination, the initializer expression is used to initialize the destination
object. [Example: T x = T(T(T())); calls the T default constructor to initialize x. — end
example ]
In your code snippet "Value{}" is a prvalue and it has the same type than the destination object, thus it is directly initialized, without having any copy or move operation involved. This is why that code can be compiled in C++17 but not in C++11.
Only for completeness, the introduced changes were part from this paper:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0135r1.html

When initializing an atomic class member it requires a 'deleted' function, but adding it would make it no longer trivially copyable

When initializing an atomic class member it requires a 'deleted' function, but adding it would make it no longer trivially copyable which is a requirement for an object/struct to be atomic. Am I just not understanding how to do this correctly, or is this a problem in the c++ standard?
Take the example below:
#include <atomic>
#include <cstdint>
template<typename T>
struct A
{
T * data;
std::atomic<uintptr_t> next;
};
template<typename T>
class B
{
std::atomic<A<T>> myA;
public:
B ( A<T> & a ) noexcept
{
myA.store(a, std::memory_order_relaxed );
}
};
int main ()
{
A<int> a;
B<int> b(a);
return 0;
}
Trying to compile this with g++ gives error: use of deleted function 'A<int>::A(const A<int>&)' myA.store(a, std::memory_order_relaxed);. My understanding of this error is that the atomic::store method is looking for that constructor in my struct A but not finding it.
Now here is what happens when I add that constructor:
#include <atomic>
#include <cstdint>
template<typename T>
struct A
{
T * data;
std::atomic<uintptr_t> next;
A(const A<T>& obj) { }
A( ) { }
};
template<typename T>
class B
{
std::atomic<A<T>> myA;
public:
B ( A<T> & a ) noexcept
{
myA.store(a, std::memory_order_relaxed );
}
};
int main ()
{
A<int> a;
B<int> b(a);
return 0;
}
I no longer receive the above compiler error but a new one coming from the requirements of the atomic class required from 'class B<int>' .... error: static assertion failed: std::atomic requires a trivially copyable type ... In other words by adding the used-defined constructors I have made my struct A a non-trivially copyable object which cannot be initialized in class B. However, without the user-defined constructors I cannot use the store method in myA.store(a, std::memory_order_relaxed).
This seems like a flaw in the design of the std::atomic class. Now maybe I am just doing something wrong because I don't have a lot of experience using C++11 and up (I'm old school). Since 11 there have been a lot of changes and the requirements seem to be a lot stricter. I'm hoping someone can tell me how to achieve what I want to achieve.
Also I cannot change std::atomic<A<T>> myA; to std::atomic<A<T>> * myA; (changed to pointer) or std::atomic<A<T>*> myA;. I realize this will compile but it will destroy the fundamental design of a class I am trying to build.
The problem here resides in the fact that std::atomic requires a trivially copiable type. This because trivially copyable types are the only sure types in C++ which can be directly copied by copying their memory contents directly (eg. through std::memcpy). Also non-formerly trivially copyable types could be safe to raw copy but no assumption can be made on this.
This is indeed important for std::atomic since copy on temporary values is made through std::memcpy, see some implementation details for Clang for example.
Now at the same time std::atomic is not copy constructible, and this is for reasonable reasons, check this answer for example, so it's implicitly not trivially copyable (nor any type which contains them).
If, absurdly, you would allow a std::atomic to contain another std::atomic, and the implementation of std::atomic contains a lock, how would you manage copying it atomically? How should it work?

std::map of non-movable objects [duplicate]

The following code will not compile on gcc 4.8.2.
The problem is that this code will attempt to copy construct an std::pair<int, A> which can't happen due to struct A missing copy and move constructors.
Is gcc failing here or am I missing something?
#include <map>
struct A
{
int bla;
A(int blub):bla(blub){}
A(A&&) = delete;
A(const A&) = delete;
A& operator=(A&&) = delete;
A& operator=(const A&) = delete;
};
int main()
{
std::map<int, A> map;
map.emplace(1, 2); // doesn't work
map.emplace(std::piecewise_construct,
std::forward_as_tuple(1),
std::forward_as_tuple(2)
); // works like a charm
return 0;
}
As far as I can tell, the issue isn't caused by map::emplace, but by pair's constructors:
#include <map>
struct A
{
A(int) {}
A(A&&) = delete;
A(A const&) = delete;
};
int main()
{
std::pair<int, A> x(1, 4); // error
}
This code example doesn't compile, neither with coliru's g++4.8.1 nor with clang++3.5, which are both using libstdc++, as far as I can tell.
The issue is rooted in the fact that although we can construct
A t(4);
that is, std::is_constructible<A, int>::value == true, we cannot implicitly convert an int to an A [conv]/3
An expression e can be implicitly converted to a type T if and only if the declaration T t=e; is well-formed,
for some invented temporary variable t.
Note the copy-initialization (the =). This creates a temporary A and initializes t from this temporary, [dcl.init]/17. This initialization from a temporary tries to call the deleted move ctor of A, which makes the conversion ill-formed.
As we cannot convert from an int to an A, the constructor of pair that one would expect to be called is rejected by SFINAE. This behaviour is surprising, N4387 - Improving pair and tuple analyses and tries to improve the situation, by making the constructor explicit instead of rejecting it. N4387 has been voted into C++1z at the Lenexa meeting.
The following describes the C++11 rules.
The constructor I had expected to be called is described in [pairs.pair]/7-9
template<class U, class V> constexpr pair(U&& x, V&& y);
7 Requires: is_constructible<first_type, U&&>::value is true and
is_constructible<second_type, V&&>::value is true.
8 Effects: The
constructor initializes first with std::forward<U>(x) and second with
std::forward<V>(y).
9 Remarks: If U is not implicitly convertible to
first_type or V is not implicitly convertible to second_type this
constructor shall not participate in overload resolution.
Note the difference between is_constructible in the Requires section, and "is not implicitly convertible" in the Remarks section. The requirements are fulfilled to call this constructor, but it may not participate in overload resolution (= has to be rejected via SFINAE).
Therefore, overload resolution needs to select a "worse match", namely one whose second parameter is a A const&. A temporary is created from the int argument and bound to this reference, and the reference is used to initialize the pair data member (.second). The initialization tries to call the deleted copy ctor of A, and the construction of the pair is ill-formed.
libstdc++ has (as an extension) some nonstandard ctors. In the latest doxygen (and in 4.8.2), the constructor of pair that I had expected to be called (being surprised by the rules required by the Standard) is:
template<class _U1, class _U2,
class = typename enable_if<__and_<is_convertible<_U1, _T1>,
is_convertible<_U2, _T2>
>::value
>::type>
constexpr pair(_U1&& __x, _U2&& __y)
: first(std::forward<_U1>(__x)), second(std::forward<_U2>(__y)) { }
and the one that is actually called is the non-standard:
// DR 811.
template<class _U1,
class = typename enable_if<is_convertible<_U1, _T1>::value>::type>
constexpr pair(_U1&& __x, const _T2& __y)
: first(std::forward<_U1>(__x)), second(__y) { }
The program is ill-formed according to the Standard, it is not merely rejected by this non-standard ctor.
As a final remark, here's the specification of is_constructible and is_convertible.
is_constructible [meta.rel]/4
Given the following function prototype:
template <class T>
typename add_rvalue_reference<T>::type create();
the predicate condition for a template specialization is_constructible<T, Args...> shall be satisfied if and only if the following variable definition would be well-formed for some invented variable t:
T t(create<Args>()...);
[Note: These tokens are never interpreted as a function declaration. — end note] Access checking is performed as if in a context unrelated to T and any of the Args. Only the validity of the immediate context of the variable initialization is considered.
is_convertible [meta.unary.prop]/6:
Given the following function prototype:
template <class T>
typename add_rvalue_reference<T>::type create();
the predicate condition for a template specialization is_convertible<From, To> shall be satisfied if and
only if the return expression in the following code would be well-formed, including any implicit conversions
to the return type of the function:
To test() {
return create<From>();
}
[Note: This requirement gives well defined results for reference types, void types, array types, and function types. — end note] Access checking is performed as if in a context unrelated to To and From. Only
the validity of the immediate context of the expression of the return-statement (including conversions to
the return type) is considered.
For your type A,
A t(create<int>());
is well-formed; however
A test() {
return create<int>();
}
creates a temporary of type A and tries to move that into the return-value (copy-initialization). That selects the deleted ctor A(A&&) and is therefore ill-formed.

Why constexpr data members are not implicitly static?

if you do this:
constexpr int LEN = 100;
LEN variable defined as const without need of typing const keyword.
It also have static storage, without need to type static keyword.
From the other hand, if we do same in class:
struct A{
constexpr static int SIZE = 100;
};
SIZE is still defined as const without need of typing const keyword,
However SIZE is not static data member.
You need to type static explicitly. If you don't there will be compilation error.
Question is:
What is the reason of need to explicitly type static?
static doesn't have same signification in both context :
for LEN, static means "only available in this compilation unit", so only internal linkage. It's a storage specifier
for A::SIZE, static means "it's a class member", so not bound to specific instances
constexpr in class context can refer to instance or class member or function, so compiler can't determine at your place if it's static or not, ie bound or not to a specific instance. It's same reasoning as const specifier. But, as you can imagine, it's a non-sense to have a non-static constexpr member, so it's forbidden. Example :
class A
{
int a;
constexpr A(int value): a(value) {}
// constexpr bound to a specific instance
constexpr int getDouble() const
{ return a*2; }
// constexpr not bound to a specific instance
static constexpr int getDouble(int b)
{ return b*2; }
}
constexpr in global context refers to something which will be calculated at compile time (or, for function, if not possible to calculate at compile time, which will be inlined), so no need of external linkage and so, comparable behavior as a static global variable or function (only comparable because, with compile time calculation or inlining, you also don't need internal linkage)
constexpr int a = 5; // Will be replace everywhere by value
/* If b is constexpr, calcul are done at compile time and result will be used
* else double is inlined, so no need of linkage at all
*/
constexpr int getDouble(int b)
{ return b * 2; }
constexpr should not imply static, because having constexpr
without static makes sense. Consider:
#include <iostream>
struct Dim
{
constexpr Dim(int a,int b) : a(a), b(b) {}
constexpr int Prod() const { return a*b; }
int a,b;
};
int main()
{
constexpr Dim sz(3,4);
int arr[ sz.Prod() ];
std::cout << sizeof(arr) << std::endl;
}
It should also not imply static outside of class definition
since static there means 'local to translation unit' and constexpr
does not require that.
I think you are confused about what static means at global scope, and your question is based on that misunderstanding.
LEN variable defined as const without need of typing const keyword.
Of course constexpr implies const, that shouldn't be surprising.
It also have static storage, without need to type static keyword.
N.B. a global variable always has static storage, because its lifetime is global. Adding the static keyword does not change that, what it does is give it internal linkage meaning it is not accessible by name outside the current translation unit.
That's the same rule for constexpr and const on global variables: a namespace-scope const variable implicitly has internal linkage (which is one of the many meanings of "static").
But a class-scope const variable does not have internal linkage, even if you add static to it. Marking a variable static means something completely different at namespace-scope and class-scope. It doesn't make sense to automatically add static to class members marked const or constexpr because that would mean something completely different than it does to variables at namespace-scope.
So constexpr implies const (obviously), and at namespace scope const implies internal linkage.
At class scope constexpr still implies const, but that doesn't have any effect on whether a member variable is a "class variable" or an "instance variable".

C++11 implicit copy constructor while implementing explicitly a constructor

I ran into a problem. I implemented a constructor for a class, but why are implicitly generated the other constructors, like the copy one?
I thought, that if I define a constructor explicitly, then the compiler doesn't generates implicitly other ones. I'm really hoping, that this a VC++ specific thing, and that this code doesn't conforms to ISO:IEC C++11:
class Foo
{
int bar;
public:
Foo(int&& arg) : bar(arg) { cout << "RConstruction" << endl; }
};
int main(int, const char*[])
{
Foo f = Foo(42);
/* Create unused temporary on the stack */
Foo::Foo(f); /* calling Foo::Foo(const Foo&): this shouldn't work... */
return (0);
}
Please keep in mind, that this is a sample code, created exactly for this situation, for demonstration purposes, I expect answers only that strictly relate to this question.
That's not a move constructor, so it doesn't suppress any implicit ones.
Just like Foo(const int&) isn't a copy constructor, Foo(int&&) isn't a move constructor, it's just a constructor taking an rvalue reference.
A move constructor looks like one of:
Foo(Foo&&)
Foo(const Foo&&)
Foo(volatile Foo&&)
Foo(const volatile Foo&&)
I thought, that if I define a constructor explicitly, then the compiler doesn't generates implicitly other ones.
If you define any constructor the compiler doesn't generate the default constructor, but it still generates the others. Define the as deleted if you don't want them:
Foo(const Foo&) = delete;
Foo& operator=(const Foo&) = delete;
You did not declare a move constructor, but a regular constructor : no implicit constructor will be deleted.
A move constructor would be of the form Foo(Foo&& arg) (with any cv-qualifier on the argument)
Also note that this statement is not valid C++ :
Foo::Foo(f);
Maybe you meant :
Foo g = Foo(f);

Resources