Re: C++0x: unfortunate interaction of rvalue references and concepts
On Dec 8, 11:54 pm, Rodrigo <rcc.d...@gmail.com> wrote:
On 6 dic, 15:06, SG <s.gesem...@gmail.com> wrote:
I noticed some unfortunate gotchas when using both features (rvalue
references and concepts/templates) which I'm going to mention here.
Most importantly: The current spec draft seems to contain such a bug
in the way std::vector (and possibly other containers as well) are
defined:
in 23.2.6 (page 826 of N2798.pdf) we have
requires AllocatableElement<Alloc, T, const T&>
void push_back(const T& x);
requires AllocatableElement<Alloc, T, T &&>
void push_back(T&& x);
[...] In case of non-copyconstructible types it's possible
to call push_back on lvalues that are possibly destroyed because only
push_back(T&&) is the only function in the overload resolution set.
[...]
If that is what happens, this is of course a bug. Using
std::HasConstructor1 (ConceptGCC's workaround for variadic
HasConstructor concept) doesn't do this, so the problem would be in
the concept specificacion of AllocatableElement.
I'm going to assum that by "bug" you mean an errornous specification
draft (as opposed to ConceptGCC bug). Just to make it clear: I
actually expect AllocatableElement to be just a an allocator-based
version of HasConstructor. So the behaviour should be similar to what
the following piece of code does:
class NonCopyable {
NonCopyable(NonCopyable const&); // = delete
NonCopyable& operator(NonCopyable const&); // = delete
public:
NonCopyable() {}
NonCopyable(NonCopyable && x); // move-constructor
};
// !CopyConstructible<NonCopyable>
// MoveConstructible<NonCopyable>
template<typename T>
class Clazz {
public:
requires std::CopyConstructible<T>
void push_back(T const&); // #1
requires std::MoveConstructible<T>
void push_back(T &&); // #2
};
int main() {
Clazz<NonCopyable> cont;
cont.push_back(NonCopyable()); // OK, #2
NonCopyable x;
cont.push_back(x); // OK, #2 AS WELL
}
I tested this is with the ConceptGCC (svn 727). But as far as I can
tell it's the fault of ConceptGCC. I believe it is the expected
behaviour w.r.t. the current specification draft: Rvalue references
CAN bind to Lvalues. For safety reasons the language+library design
should make push_back(x) ill-formed because it may implicitly move the
object without having to use std::move(x). There's no more attractive
overload because the CopyConstructible requires clause prevents the
first function from participating in overload resolution.
So, we either have to make the addition of
private:
requires !std::CopyConstructible<T> // note the "!"
void push_back(T const&); // = delete
well-formed (provided that it isn't already -- ConceptGCC complains
about not being able to overload both push_back(T const&) versions
despite the differing requirement clauses) or just make rvalue
references only bind to rvalues.
I currently favour the latter approach: making rvalue references only
bind to rvalues. Of course explicit conversion a la static_cast<A&&>
(x) should still be possible for std::move. This approach will turn
buggy code into ill-formed code as opposed to code that is well-formed
but has unintentional possibly fatal behaviour. It would not hinder
perfect forwarding due to template parameter deduction and reference
collapsing rules. The meaning of "&&" would be more constistent
(compare it with ref qualifiers for non-static memberfunction and the
semantic of "&&" in concept definitions).
Cheers!
SG
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]