Re: Template setters
The "setter's designer" /must/ know design details of class she designs
and certainly the type of particular attribute of class that particular
method modifies. Also she should know the types of arguments that are
passed to all interface of the class. If part of the interface is template
then it should verify that the concepts match. How else? Does she deserve
to be freed of that knowledge?
I think you mistakenly assumed I was talking about about knowing the
class the setter is in. Correct me if I'm wrong on this. To clarify -
I meant the type of the variable member being set. The only knowledge
required is the knowledge of the availability of operator =, which is
also the case in "traditional" setters. Let's bring back the example:
template<class T>
void setWidget(T &&widget)
{
mWidget = std::forward<T>(widget);
}
As you can see, there is not a single type specification anywhere.
The setter only assumes some overload of operator = exists (not
exactly true strictly speaking, because when the setter is not used,
there is no need for operator = to exist and compilation goes on just
fine, but that's a case when the setter shouldn't exist in such form
in the first place). The setter designer is indeed freed from any
knowledge of the type of mWidget, especially what overloads of its
operator = exist. Note, that if mWidget is a template type itself,
such setter is pretty much the only way to provide generic setting
mechanism. That alone implies this kind of setter is as type agnostic
as it can be.
The "setter's user" should know the types of arguments that may be
passed to the part of interface she uses. She may not know the types
used internally in class ... that is called "encapsulation". That is
broken since the interface depends on overloads of 'operator=' and
the errors about those are the only indication of types of expected
arguments to your setters.
You have a point there, but I think only partially. The setter alone
/could/ require the knowledge of the type of the member being set.
But that knowledge is usually already passed to the user in the form
of associated getter return type, documentation, etc. If such
knowledge is not available, template setter can indeed be a bad idea.
That logic leaves impression that you try to free class designers
from knowing (and caring about) the types of components of class?
By all means, no. The setter is only one use case of a member. Your
implication would be true if there was noting more using that member,
than the setter or equivalent. And we do have such situations - STL
containers, for one example. They have the equivalents in the form of
emplace() functions. You can try to extend it onto whole family of
classes containing templated members, only relying on the type
properties, as concepts tend to provide.
Again: "setter" will be decoupled from type it sets? What is the
benefit?
Taking into account every available operator = overload, without code
duplication; including modern language features like move semantics.
Also being future-proof.
IOW if I needed what you wrote then I would write it like that:
void copyToTheX( XPtr const& x )
{
theX.reset( x ? new X(*x) : nullptr );
}
void moveToTheX( XPtr&& x )
{
theX = x;
}
That is a valid approach, but very limiting. What if someone wants
to set nullptr to theX? Which one should he use? None logically seems
appropriate. Would you write another function, setTheX/setTheXPointer
or similar? What if you wanted to take advantage of other possible
operator = overloads (let's assume there are some)? More functions,
with possibly more code duplication? When all pretty much logically
/set/ theX to the parameter. One template setter does all that. (I
also assumed you meant using std::move() in moveToTheX(), because now
it's trying to make a copy).
Variable /was/ set to new value and it was not copied anywhere.
Argument was not moved but ... so what? It was not damaged in any way.
Variable was indeed set to the new value, but with two dangerous
assumptions:
1. The underlying type X is copyable and the possible side effects of
making such copy are not dangerous or otherwise undesired.
2. The original value is safe to be freed, with possible side effects
also not being dangerous or otherwise undesired. Additional
assumption is that the original pointer is not stored anywhere in
raw form.
Then why not to enforce it with 'T&&' parameter?
If you meant unique_ptr&& here, you could enforce it, but the
problems with not utilizing other possible operator = overloads and
being future-proof still remain.
I do not use such /assumptions/ whatsoever. X was clearly /declared/
copyable in my code. For interface maker it /must/ be clear.
Yes it was clearly declared copyable. But when writing the setter you
/have to/ check it and tie yourself to what you see there in that
very moment. Why then not write it in a generic way and be certain
your code will work, regardless of X? The only way it will produce a
compile error is when a user misuses it. But the knowledge of the
type the user uses is natural.
You did bring 'unique_ptr' as example of movable but not copyable
type yourself?
Yes, to show how that coupling with the type works, hence my reply.
I do not expose what I internally use, ever. If the class uses
internally 'auto_ptr' or raw pointer (for whatever legacy reasons)
or 'shared_ptr' (since my side of interface actually uses it in
shared way) then I may still use move-only interface with
'unique_ptr'. The concerns of implementer of class and user of class
should be separated.
That means you are keeping legacy interface. That is valid as long as
it is possible. I'm not disputing keeping the interface invariant as
this is a good practice. I'm suggesting that this might not be
possible and might become logically awkward.
I repeat you did bring 'unique_ptr' as example of movable but not
copyable type yourself? It was example as reply to that. If you need
example about something else then ask away.
The example was good, since it presents a situation, which introduces
potential problems. Solving problematic situations can show the
validity (or not) of the idea being presented.
How often you need to switch between 'shared_ptr' and 'unique_ptr' as
internal design detail? For me those types were exactly made to get rid
of loose concept of C pointer. Raw pointer sometimes means
'unique_ptr' for single element, sometimes 'unique_ptr' for possibly
several elements, sometimes 'shared_ptr', sometimes 'weak_ptr',
sometimes 'vector' and sometimes 'iterator' and so on. However the
concepts are so far that it is unlikely that we need to switch it.
In this case, the pointer example could be dealt with as you say. So
let's assume some other type exhibiting such problems.
Also, If I need to switch the internal type then why should that
affect interface? If it was 'unique_ptr' then it stays 'unique_ptr'
even if I internally switch from 'auto_ptr' to 'vector'. These
two should be decoupled.
It shouldn't when it's possible. But a problem arises when it's not
possible or logically correct. And what of setting the now-new type?
Would you introduce a new setter taking it, leading to inconsistency
or not introduce it, leading to not being able to use the correct
method of setting the value? Why should you make that decision at all
when a template setter already solves it by design?
So there are no templated setters. There even are no overloaded setters.
Yes, but notice they use concrete primitive types and need not be
templated.
"The trick" seems to be used also very rarely
It is used when we don't know the type. The interface then needs to
be designed in a way that is type-agnostic and non-limiting (as much
as it can). These are the properties I wish to incorporate into the
setter idiom.
On the other hand it sometimes uses overloads to allow implementers to
have different code in bodies:
That is a valid case to use specific overload, not a generic
template.
Certainly the authors do care.
I would sey - they do care /sometimes/ and /to some extent/. The
technique used is such places take advantage of metaprogramming to
introduce safer code. Template setters are just value forwarders, so
there is little need for deep type inspection.
What you mean? No way. Standard requires things like "T is
EmplaceConstructible into X from args. For vector and deque, T is also
MoveInsertable into X and MoveAssignable." for emplace method of sequence
containers. Good library implementation certainly checks such traits.
Template setter requires the type to be CopyAssignable or
MoveAssignable. Same situation here. It does not enforce any of
these.
Standard may leave it loose for allowing low quality implementations
by saying that "not fulfilling requirements is undefined behavior" but
I can not follow that practice when writing my code. I can not say
to my end user that she may not enter value "42" to textbox "A" and
that it is "undefined behavior" (sometimes works sometimes not)
when she does. That is unthinkable.
Hold on here. Nobody is saying anything about undefined behavior. We
are saying about compile-time polymorphism leading to either perfect
forwarding to appropriate operator = overload or compile-time error.
Nothing is left undefined. It's like writing every possible setter
overload in one place.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]