Le 24/04/15 10:20, David Rodríguez Ibeas a écrit :
> Vicente raises an interesting point, are there any preconditions that
> might not be testable/accessible by the client? My experience is that
> I have hit this in some cases, with interfaces that require that
> 'stop' not be called unless 'start' has previously been called and
> that at the same time do not provide an accessor to whether the object
> had ever been started. But as Jonas mentions, the feeling when working
> with those APIs is that they were not well designed and imposed a
> burden on the user that has to track externally whether 'start' was
> called or not.
>
> I don't have any example of a component that I would consider well
> designed and at the same time did not allow the client to determine
> before hand whether a given call would be a violation of the contract.
The standard contains a contextual contract. tsd::mutex::unlock must be
called by the same thread that locked it.
>
> As a side note, is the precondition really that you cannot do anything
> other than assignment/destruction on a moved-from string?
Yes.
> My believe was that "valid" meant that you can call any member
> function with no preconditions and then any member function for which
> preconditions are met. So this would be a perfectly valid program:
>
> std::string src = "String that might or not fit SMO";
> std::string dst = std::move(src);
> if (!src.empty()) {
> std::cout << "First character after move: " << src[1] << '\n';
> }
>
> Which again falls within what Jonas mentioned: the caller is able to
> determine whether a call to 'operator[]' will be within contract by
> calling other member functions that have wide contracts. The
> precondition on 'operator[]' can be expressed in terms of the publicly
> available 'src.size()'.
>
There is a long thread about this (see [std-discussion] side effects of
moving standard types)
> Whether you believe that it is sane or not to try to reuse a string
> after being moved this way is a different question, but the contracts
> for the different members should not depend on what happened in the
> past, but the current state of the object. If the string after being
> moved is empty you cannot access the 1st element, how the string
> became empty is not part of the contract. If the string is non-empty,
> it does not matter whether move was called before, it is not empty and
> the call is within contract.
>
> If you want to add additional diagnostics specifically for this (using
> an object that has been moved-from), you could use an extension
> similar to checked iterators for containers. In that extension, the
> test could either be internal or the additional state would have to be
> accessible for the caller to verify.
The particular case of the move operation could be managed by compile
time pre/post conditions as suggested by Andrzej. Andzej suggest the use
of properties as e.g.
template <typename T>
property bool is_partially_formed (T const&);
T&& move(T& t) [[post: is_partially_formed(t) ]]
X& X::X(const& X x) [[pre: ! is_partially_formed(x) post: !
is_partially_formed(*this) ]]
Any other operation than destruction and assignment
R X::f() [[pre: ! is_partially_formed(*this) ]]
X x; // doesn't have property is_partially_formed yet.
X y = std::move(x); // x has property is_partially_formed now.
x = makeX(); // x doesn't have property is_partially_formed anymore
Vicente