Topic: MoveConstructible question
Author: Rodrigo <rcc.dark@gmail.com>
Date: Fri, 24 Apr 2009 10:39:28 CST Raw View
Hey!
As far as I know you are right. I noticed the same while checking the
latest concept papers.
The big problem seems to be that an implicit function T(T&&) is -
always- generated by default and delegates to T(const T&) to do its
job, in other words:
// very simplified concepts to illustrate the problem
auto concept HasConstructor<typename T, typename Arg> {
T::T(Arg);
}
auto concept MoveConstructible<typename T> : HasConstructor<T, T&&>
{ }
auto concept CopyConstructible<typename T> : HasConstructor<T, const
T&> { }
struct T1 {
T1(const T1&); // enough to satisfy MoveConstructible, default-
generated T(T&&) delegates to this
};
struct T2 {
T2(T2&&) = delete; // doesn't satisfy MoveConstructible
};
struct T3 {
const T3(const T3&) = delete; // doesn't satisfy
MoveConstructible, default-generated T(T&&) failed implementation
};
Given this, I don't know how to solve your problem =\ its pretty hard
to deal with magically generated functions (explicit "= delete" is
needed).
I found other weird things in the standard concepts (for example
NothrowDestructible). I recall that for this last case, the committee
said that a throwing destructor should be rare (at least inexistent in
the standard library) so actually requiring an empty throw
specification was an overkill. If I could, I would simply remove the
NothrowDestructible from the standard library or I would make it an
explicit concept.
On 16 abr, 18:14, SG <s.gesem...@gmail.com> wrote:
> Hi!
>
> As far as I can tell the concept MoveConstructible only guarantees
> that I can construct a type given an rvalue (reference). It doesn't
> imply that the type has an actual overload T::T(T&&) because the
> standard copy constructor T::T(T const&) could be selected as well for
> rvalue arguments. My question is: Can I somehow detect whether a type
> T provides this overload?
>
> Motivation for the question is to avoid code duplication (w.r.t.
> compiled code) in cases like this:
>
> template<CopyConstructible T>
> class foo
> {
> public:
> void do_something(T const&);
>
> requires std::MoveConstructible<T>
> void do_something(T &&);
> };
>
> Even if T has no special T::T(T&&) overload the 2nd do_something might
> be instantiated without any benefits -- only code bloat. In an ideal
> world I would be able to detect that and only generate the 2nd
> function in case T is optimized for move construction.
>
> Opinions?
>
> Cheers!
> SG
>
> --
> [ comp.std.c++ is moderated. To submit articles, try just posting with ]
> [ your news-reader. If that fails, use mailto:std-...@netlab.cs.rpi.edu]
> [ --- Please see the FAQ before posting. --- ]
> [ FAQ:http://www.comeaucomputing.com/csc/faq.html ]
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: SG <s.gesemann@gmail.com>
Date: Sat, 25 Apr 2009 16:09:39 CST Raw View
On 24 Apr., 18:39, Rodrigo <rcc.d...@gmail.com> wrote:
>
> As far as I know you are right. I noticed the same while checking the
> latest concept papers.
> The big problem seems to be that an implicit function T(T&&) is -
> always- generated by default and delegates to T(const T&) to do its
> job, in other words:
Move-constructors are never automatically generated anywhere. I think
you may be confusing something (understandably, it took me quite a
while to grok concepts).
"concept checking" is done in two places. 1. checking code of a
constrained template against requirements. 2. for checking
concept_maps against requirements.
As for the first part (restricted template <-> concepts), the compiler
will assemble "archetypes" depending on the constraints. These
archetypes will have a deleted copy construction and assignment
functions unless there are are explicit requirements for those
mentioned in some constraint. Type checking of the constrainted
templates' code is done via replacing type parameters with these
"archetypes".
As for the second part (concept_map <-> concepts), associated function
requirements work more like "invocation signatures". The requirement
implies an expression that has to be well-formed. The actual function
declaration in a concept_map doesn't have to match the requirement
perfectly as long as this implied "invocation expression" is well-
formed (might involve implicit conversions of parameters and return
type, etc).
> // very simplified concepts to illustrate the problem
> auto concept HasConstructor<typename T, typename Arg> {
> T::T(Arg);
> }
>
> auto concept MoveConstructible<typename T> : HasConstructor<T, T&&>
> { }
> auto concept CopyConstructible<typename T> : HasConstructor<T, const
> T&> { }
>
> struct T1 {
> T1(const T1&); // enough to satisfy MoveConstructible
> };
Yes, that's because the MoveConstructible requirement only says
constructing from rvalue expressions needs to be possible. Since
rvalues bind to a reference-to-const, T1 is MoveConstructible.
> struct T2 {
> T2(T2&&) = delete; // doesn't satisfy MoveConstructible
> };
T2::T2(T2&&) doesn't qualify as a copy constructor as far as the
standard is concered (because it's not an lvalue reference argument).
This makes the compiler generate a default copy constructor T2::T2(T2
const&). so T2 is MoveConstructible as well as CopyConstructible.
> struct T3 {
> const T3(const T3&) = delete; // doesn't satisfy
> // MoveConstructible, default-generated T(T&&) failed implementation
> };
Here, T3::T3(const T3&) qualifies as a copy constructor. It is deleted
however. So T3 is neither MoveConstructible nor CopyConstructible.
> Given this, I don't know how to solve your problem =\ its pretty hard
> to deal with magically generated functions (explicit "= delete" is
> needed).
It's not really a "problem" ... more of an observation that it seems
it's not possible to detect the actual existence of rvalue reference
overloads via concepts. So, for containers like std::vector<int> you
might end up with two push_back functions whereas we only need one in
some cases.
> I found other weird things in the standard concepts (for example
> NothrowDestructible). I recall that for this last case, the committee
> said that a throwing destructor should be rare (at least inexistent in
> the standard library) so actually requiring an empty throw
> specification was an overkill. If I could, I would simply remove the
> NothrowDestructible from the standard library or I would make it an
> explicit concept.
I think this is subject to change anyways, see
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2855.html
which proposes another keyword "noexcept". A function "modifier" which
can be statically checked (including link-time checking, hopefully)
and promises a non-throwing function.
Cheers!
SG
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Rodrigo <rcc.dark@gmail.com>
Date: Sun, 26 Apr 2009 05:30:12 CST Raw View
On 25 abr, 17:09, SG <s.gesem...@gmail.com> wrote:
> On 24 Apr., 18:39, Rodrigo <rcc.d...@gmail.com> wrote:
>
>
> Move-constructors are never automatically generated anywhere. I think
> you may be confusing something (understandably, it took me quite a
> while to grok concepts).
yeah what I said is stricly incorrect, a simple proof:
struct a { };
a& (a::*pmf)(a&&) = &a::operator=; // this fails
however, for concepts, I find it to be a simple way to understand how
it works in general.
> > struct T2 {
> > T2(T2&&) = delete; // doesn't satisfy MoveConstructible
> > };
>
> T2::T2(T2&&) doesn't qualify as a copy constructor as far as the
> standard is concered (because it's not an lvalue reference argument).
> This makes the compiler generate a default copy constructor T2::T2(T2
> const&). so T2 is MoveConstructible as well as CopyConstructible.
ConceptGCC appears to disagree with this (with the sample concepts I
posted), but I know ConceptGCC is somewhat old (even if its just one
year behind) and many changes in wording have occurred so far. If any
change in that area has occurred then you are in better position to
understand it than I do :) but if what ConceptGCC does remains legal,
the "default-generated move-constructor for concepts" idea could
explain it in an easy way, if technically incorrect.
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: SG <s.gesemann@gmail.com>
Date: Mon, 27 Apr 2009 14:41:32 CST Raw View
On 26 Apr., 13:30, Rodrigo <rcc.d...@gmail.com> wrote:
> On 25 abr, 17:09, SG <s.gesem...@gmail.com> wrote:
> > On 24 Apr., 18:39, Rodrigo <rcc.d...@gmail.com> wrote:
>
> > > struct T2 {
> > > T2(T2&&) = delete; // doesn't satisfy MoveConstructible
> > > };
>
> > T2::T2(T2&&) doesn't qualify as a copy constructor as far as the
> > standard is concered (because it's not an lvalue reference argument).
> > This makes the compiler generate a default copy constructor T2::T2(T2
> > const&). so T2 is MoveConstructible as well as CopyConstructible.
>
> ConceptGCC appears to disagree with this (with the sample concepts I
> posted), but I know ConceptGCC is somewhat old (even if its just one
> year behind) and many changes in wording have occurred so far.
See N2857.pdf, 12.8 [class.copy], paragraph 2. The footnote 113 in
paragraph 2 says:
" Because a template constructor or a constructor whose first
parameter is an rvalue reference is never a copy constructor, the
presence of such a constructor does not suppress the implicit
declaration of a copy constructor. Such constructors participate in
overload resolution with other constructors, including copy
constructors, and, if selected, will be used to copy an object. "
Read: Even though a templated constructor can be used to copy objects
as well as a constructor taking an rvalue reference can "copy"
rvalues, such constructors are not considered "copy constructor"-ish
enough to suppress the generation of a default copy-c'tor.
I can't reproduce anything that would suggest ConceptGCC does
something wrong in this regard. I tested this example:
#include <concepts>
struct S {
S() {}
private:
S(S&&);
};
template<typename T>
requires std::CopyConstructible<T>
&& std::DefaultConstructible<T>
void foo() {
S s1;
S s2 = s1;
}
void bar() {
foo<S>();
}
ConceptGCC compiles this without any complaint. So, it does seem to do
the right thing here. S is CopyConstructible. Since CopyConstructible
is a refinement of MoveConstructible S is also MoveConstructible.
Cheers!
SG
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Rodrigo <rcc.dark@gmail.com>
Date: Tue, 28 Apr 2009 12:19:39 CST Raw View
On 27 abr, 15:41, SG <s.gesem...@gmail.com> wrote:
> On 26 Apr., 13:30, Rodrigo <rcc.d...@gmail.com> wrote:
> I can't reproduce anything that would suggest ConceptGCC does
> something wrong in this regard. I tested this example:
>
> #include <concepts>
>
> struct S {
> S() {}
> private:
> S(S&&);
> };
>
> template<typename T>
> requires std::CopyConstructible<T>
> && std::DefaultConstructible<T>
> void foo() {
> S s1;
> S s2 = s1;
> }
>
> void bar() {
> foo<S>();
> }
replace std::CopyConstructible<T> with std::MoveConstructible<T> and
it fails (yes it does).
The reason:
// definition of some concepts in bits/concepts.h from conceptgcc
auto concept MoveConstructible<typename T> : HasConstructor1<T, T&&>
{ }
/// @brief Describes types that can be copy-constructed
/// TODO: Should refine MoveConstructible, but there's a problem
/// with the move-constructibility of references. Also, should
/// refine HasConstructor1<T, const T&>, but we're running into an
/// ambiguity.
auto concept CopyConstructible<typename T>
{
T::T(const T&);
}
Thats why I said "using my sample concepts" in my reply above. I'm not
sure if the move-constructibility of references problem mentioned
there is a mere implementation bug or something else, probably you
know the answer.
If the problem is an implementation bug, you could answer your
original question by using HasConstructor.
I'm not adding anything else to this thread, just tried to help.
cheers
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: SG <s.gesemann@gmail.com>
Date: Wed, 29 Apr 2009 01:02:33 CST Raw View
On 28 Apr., 20:19, Rodrigo <rcc.d...@gmail.com> wrote:
> On 27 abr, 15:41, SG <s.gesem...@gmail.com> wrote:
> > I can't reproduce anything that would suggest ConceptGCC does
> > something wrong in this regard. I tested this example:
>
> > #include <concepts>
>
> > struct S {
> > S() {}
> > private:
> > S(S&&);
> > };
>
> > template<typename T>
> > requires std::CopyConstructible<T>
> > && std::DefaultConstructible<T>
> > void foo() {
> > S s1;
> > S s2 = s1;
> > }
>
> > void bar() {
> > foo<S>();
> > }
>
> replace std::CopyConstructible<T> with std::MoveConstructible<T> and
> it fails (yes it does).
You're right. My mistake. Actually it SHOULD fail with both concepts
because S is not constructible from an rvalue. That makes S is neither
MoveConstructible nor CopyConstructible according to the current
draft. But as far as I can tell it should satisfy HasConstructor<S,S
const&> due to the compiler-generated copy constructor.
Anyhow, a deleted move constructor doesn't make much sense in
practice.
> [...] I'm not
> sure if the move-constructibility of references problem mentioned
> there is a mere implementation bug or something else, probably you
> know the answer.
No, I don't know it. As far as I can tell the current draft renders
lvalue references neither move-constructible nor copy-constructible
due to an associated requirement in MoveConstructible<T>. In case T is
an lvalue reference this associated requirement is about initializing
an lvalue reference with an rvalue reference. But there's no implicit
conversion between these types. --- I think it is a bug in the current
draft.
> If the problem is an implementation bug, you could answer your
> original question by using HasConstructor.
HasConstructor<T,T&&> for some non-reference type T only implies that
T is constructible from rvalues. It does NOT imply that T has a move
constructor. It doesn't seem possible to detect whether a type T has
an actual move-constructor. This isn't a big problem but it might lead
to code duplication with no benefits (i.e. two push_back version, one
for lvalues, one for rvalues -- even though the value type doesn't
have a move constructor). This was my observation I wanted to share.
Cheers!
SG
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: David Abrahams <dave@boostpro.com>
Date: Fri, 1 May 2009 11:09:41 CST Raw View
on Thu Apr 16 2009, SG <s.gesemann-AT-gmail.com> wrote:
> Hi!
>
> As far as I can tell the concept MoveConstructible only guarantees
> that I can construct a type given an rvalue (reference). It doesn't
> imply that the type has an actual overload T::T(T&&) because the
> standard copy constructor T::T(T const&) could be selected as well for
> rvalue arguments.
That's correct.
> My question is: Can I somehow detect whether a type
> T provides this overload?
Not as far as we know. However, /something/ like that may be needed
because of
http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2009/n2855.html
> Motivation for the question is to avoid code duplication (w.r.t.
> compiled code) in cases like this:
>
> template<CopyConstructible T>
> class foo
> {
> public:
> void do_something(T const&);
>
> requires std::MoveConstructible<T>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This requirement adds nothing, since as you note, CopyConstructible
implies MoveConstructible.
> void do_something(T &&);
> };
>
> Even if T has no special T::T(T&&) overload the 2nd do_something might
> be instantiated without any benefits -- only code bloat. In an ideal
> world I would be able to detect that and only generate the 2nd
> function in case T is optimized for move construction.
Hmm, interesting. I never thought of the issue in terms of how much
code gets generated. You have a point there.
--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: SG <s.gesemann@gmail.com>
Date: Thu, 16 Apr 2009 17:14:56 CST Raw View
Hi!
As far as I can tell the concept MoveConstructible only guarantees
that I can construct a type given an rvalue (reference). It doesn't
imply that the type has an actual overload T::T(T&&) because the
standard copy constructor T::T(T const&) could be selected as well for
rvalue arguments. My question is: Can I somehow detect whether a type
T provides this overload?
Motivation for the question is to avoid code duplication (w.r.t.
compiled code) in cases like this:
template<CopyConstructible T>
class foo
{
public:
void do_something(T const&);
requires std::MoveConstructible<T>
void do_something(T &&);
};
Even if T has no special T::T(T&&) overload the 2nd do_something might
be instantiated without any benefits -- only code bloat. In an ideal
world I would be able to detect that and only generate the 2nd
function in case T is optimized for move construction.
Opinions?
Cheers!
SG
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]