|
|||
|
On Tuesday, April 10, 2012 11:14:08 AM UTC-7, Alf P. Steinbach wrote:
> The compiler is able to do it within the current language, but it's > constrained by > > * the problem of aliasing, i.e. correctness violation, and > > * depending on the solution, a combinatorial explosion, and that > > * depending on the solution, the linker must support the scheme. Thanks for the analysis Alf. On the surface it looks like the separate compilation model sinks attempts to provide input value optimization as Dave Abrahams mentioned. But perhaps a really robust compiler/linker would be able to do it? Here's what I've been thinking. Let's start with something simple: - Only apply optimization to classes with non trivial constructors or POD classes of a certain size. To avoid pessimizations. - Restrict candidates for this optimization to parameters that are only copied inside the function. - If the parameter is passed by reference to any function (including constructors) it cannot be optimized. Perhaps we may want to allow const references for an aggressive optimization strategy but this would not be strictly correct/conforming. - The goal is to guarantee that when the function exits that the parameter will not be modified if it is passed by reference. Therefore we can create a single optimized version of the function call and avoid the combinatorial explosion. Alf? - Now the hand-waving part! Somehow when the linker mashes the entire executable together it needs to substitute the non-optimized calls to the optimized ones. I need some help on this part. I'll add an additional observation at this point: This optimization will never work for virtual function calls, so the pass by reference hand optimization may never be completely eliminated from the language. > A partial solution, to avoid all three of those problems, is to use > immutable types with reference semantics. Can you elaborate a little more on this point? Do you mean that if we had a concept of an immutable class that the compiler could recognize then we could more easily apply copy optimizations? > Also, for the particular case of `std::string` another partial solution > is to use a COW (Copy On Write) implementation, which I believe is still > how the implementation for g++ works. Maybe we just need copy on write objects? This may solve some copying issues. Is there anyway that std::move can help us here? For instance, can the temporary parameter object be moved to my member variable in my class constructor? -- Ross MacGregor -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|
||||
|
||||
|
|
|
|||
|
On 04/10/2012 08:00 PM, Ulrich Eckhardt wrote:
> Am 10.04.2012 11:18, schrieb rossmpublic@gmail.com: >> I have a very simple question that I have been unable to find a >> satisfactory answer. The question is why do I need to manually >> optimize my functions using const references? >> >> For example: >> >> // Optimized passing of string parameter >> Widget(std::string const& name); >> SetName(std::string const& name); >> >> // Non-optimized passing of string parameter >> Widget(std::string name); >> SetName(std::string name); > > Even worse: > > // optimized passing of int parameter > void foo(int i); > // pessimized passing of int parameter > void foo(int const& i); > // probably pessimized passing of pair > void bar(std: air<int, int> const& p);> // and what about this here?? > SetName(std::string name) { this->name.swap(name); } > > The performance of course depends on the actual implementation, but in > common implementations a reference is just a "self-dereferencing > pointer", so a reference requires another level of indirection. Also, > any function can cheat and const_cast, so a calling function must not > assume that the call doesn't modify the passed object. Making informed > predictions requires looking at the implementation of the function, > which is the reason that many modern compiler perform optimizations > across translation-unit boundaries. > > Note that the thing with the int and pair above is something I learned > from micro-optimizing some code. Certain code. On one particular > platform. In a very tight loop. I don't claim that this is universally > the most performant way. The compiler knows best what is the most efficient way of passing a value. The programmer knows best if passing by const reference is OK as a possible optimisation. So, a possibility would be to invent some syntax to tell the compiler to pick the most efficient method of passing a value either by value or by const reference. E.g. void foo(int default i) // probably equivalent to: void foo(int i) void foo(std::string default s) // probably equivalent to: void foo(std:string const &s) void foo(std: air<int, int> default p)// may depend on the platform how the pair is passed Geert-Jan Giezeman -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
On 12.04.2012 12:05, rossmpublic@gmail.com wrote:
> On Tuesday, April 10, 2012 11:14:08 AM UTC-7, Alf P. Steinbach wrote: >> The compiler is able to do it within the current language, but it's >> constrained by >> >> * the problem of aliasing, i.e. correctness violation, and >> >> * depending on the solution, a combinatorial explosion, and that >> >> * depending on the solution, the linker must support the scheme. > > Thanks for the analysis Alf. On the surface it looks like the > separate compilation model sinks attempts to provide input value > optimization as Dave Abrahams mentioned. But perhaps a really robust > compiler/linker would be able to do it? With the flags approach that I described you don't need additional linker support. But it might be premature optimization. It could be that at least for some programs it would, in total, slow things down to pass those flags everywhere, e.g. because one less register was available, or whatever. > Here's what I've been thinking. Let's start with something simple: > > - Only apply optimization to classes with non trivial constructors > or POD classes of a certain size. To avoid pessimizations. > > - Restrict candidates for this optimization to parameters > that are only copied inside the function. > > - If the parameter is passed by reference to any function (including > constructors) it cannot be optimized. Perhaps we may want to > allow const references for an aggressive optimization strategy but > this would not be strictly correct/conforming. Wrt. passing further to some reference to const formal argument, the main solution to the problem that the compiler can't guarantee the one in a trillion trillion'th case in Very Bad Code, is to let the programmer take responsibility. Leave the programmer in control. Much is impossible if the compiler must do it all alone, because, well, if it was outfitted with the necessary smarts then it would be very much too slow! But with a little help from the programmer, which is what many of the keywords in C++ are about, very little remains as impossible. E.g. in this case the help could take the form of some attribute which, if present, prevents the optimization, e.g. because there's a call to a function that modifies in spite of promising not to. Of course, it would then be a kind of absurd situation where one programmer is adding an attribute saying that another programmer (or possibly him/her self) incorrectly added "const" to some formal arg. Anyway, with such a more practical solution (programmer decides in dubious case) the "cannot be optimized" is happily not correct. And likewise, "would not be correct" is then happily not correct. It can be optimized, and it will be correct, just not wrt. the current language. With the absence of an attribute taken as general leave to optimize, an argument of type "cv T" is a candidate for optimization if, say, the Boost function that produces good argument types changes the type. Then furthermore there must be no potential modification of that argument, under the assumption (provided by absence of attribute) that "const" really means "not modified". Under these conditions the optimization is permitted for that argument. Whether copying will actually be done in any particular call, then depends on the corresponding flag passed by the caller. If the call site promises via this argument's flag that there is no possible aliasing, then copying will be avoided. And what's nice about that is that the caller can pass those flags also to a non-optimized function, which then will copy regardless of the flags. I.e. each call site does not need to know whether the function implementation supports the optimization or not -- each call site's responsibility is just to decide whether it can guarantee non-aliasing. > - The goal is to guarantee that when the function exits that the > parameter will not be modified if it is passed by reference. > Therefore we can create a single optimized version of the function > call and avoid the combinatorial explosion. Alf? Oh yes, that's simple, as I wrote. Instead of different variants of function, just a single function with a hidden flags argument. Where the flags carry each call site's knowledge of aliasing, into the function. > - Now the hand-waving part! Somehow when the linker mashes the > entire executable together it needs to substitute the non-optimized > calls to the optimized ones. I need some help on this part. Huh. > I'll add an additional observation at this point: > > This optimization will never work for virtual function calls, > so the pass by reference hand optimization may never be completely > eliminated from the language. As an exercise, imagine the optimization is done, with the flags approach. Then it just works. >> A partial solution, to avoid all three of those problems, is to use >> immutable types with reference semantics. > > Can you elaborate a little more on this point? Do you mean that if we > had a concept of an immutable class that the compiler could recognize > then we could more easily apply copy optimizations? With immutable types with reference semantics there is no copying (except of small pointer). Think Java strings. You can reassign a Java string variable, but that doesn't copy the string value. Neither is there any modification of the string value. So, the problem is then just not relevant. >> Also, for the particular case of `std::string` another partial >> solution is to use a COW (Copy On Write) implementation, which I >> believe is still how the implementation for g++ works. > > Maybe we just need copy on write objects? This may solve some copying > issues. Scott Meyers, I think it was, recently remarked that COW is not permitted for std::string under C++11 rules. I'm not sure if that's correct, but with threading in the picture it sounds quite plausible. The nice thing about one's own types is that they can be more practical wrt. safety, at least with 20-20 hindsight. Where std::string is needlessly unsafe (e.g. construction from 0), one's own type can be safe, and where std::string (reportedly) has to support safe operations in some kind of multi-thread scenario, I don't know exactly what but something that affects COW, one's own type can just happily and more practically pass the problem on to the programmer. Don't use me that way, or else add synchronization! > Is there anyway that std::move can help us here? For instance, can the > temporary parameter object be moved to my member variable in my class > constructor? I'm not sure that I understand that question. Cheers & hth., - Alf -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
On Thu, 12 Apr 2012 03:18:04 -0700 (PDT)
Geert-Jan Giezeman <geert@cs.uu.nl> wrote: > So, a possibility would be to invent some > syntax to tell the compiler to pick the most efficient method of > passing a value Done. it's called "C++". The standard afaik imposes no machine-code restriction on function call implementation. Anything that adheres to the semantics it defines is valid. --jkl -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
On Tue, 10 Apr 2012 11:14:08 -0700 (PDT)
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> wrote: > Ideally there should > be "in", "out" and "in-out" designators as in Ada We almost have that now, right? in: value or const reference out: reference in/out: reference The last doesn't deserve distinction. It's rarely used because it's rarely justified. Were "in/out" syntax part of the language, the programmer has to learn and use it, and the compiler has to enforce it. Yet it's hardly used. For instance, there's barely one in/out parameter in the STL. std::string::swap() is one uncommon example. I can't think of any that don't involve some kind of similar buffer replacement. Nothing in <algorithm> uses in/out parameters. The best functions are true functions: one output generated from N inputs. Sometimes, because of the way C++ is defined, the output takes the form of an output parameter instead of a return type. (And sometimes the return value is used to convey error status, but that's a whole different discussion!) Functions with in/out parameters, by contrast, have side-effects by design. --jkl -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
On 12.04.2012 06:08, Dave Harris wrote:
> 0xCDCDCDCD@gmx.at (Martin B.) wrote (abridged): >> However, what I would like to raise as a QOI question is, why can't >> we have (or do we?) a proper compiler warning when the compiler >> detects that the passed-by-value parameter isn't modified at all >> and really should have been passed by const-reference? > > I would not want such a warning. There are too many cases where > passing a copy is preferred. > > For example, passing an int by const reference is almost certainly > worse for performance. So, probably, is passing a pair of ints, or a > pair of pointers. If the warning depends on the size of the object, > than making a class use the pImpl idiom could trigger the warning, > as could other implementation changes. > > You should consider the issue of aliasing. It may be cheaper to pass > a 4-int rectangle by const reference, but using it may be more > expensive because it's harder for the compiler to be sure it is not > changed through an alias. > > Another issue is the need for a standard interface, for > polymorphism. Some overrides of a virtual function might change > their arguments, and others might not. All valid points! Still, there are far too many cases in our code base where devs just forget the "const&" where it would make perfectly sense. I guess it may be too advanced for a compiler. Would be interestng though if static analysis tools do such checking - maybe on a class by class base or based on other configuration options. cheers, Martin -- Good C++ code is better than good C code, but bad C++ can be much, much worse than bad C code. [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
On 12/04/2012 11:05, rossmpublic@gmail.com wrote:
> On Tuesday, April 10, 2012 11:14:08 AM UTC-7, Alf P. Steinbach wrote: >> The compiler is able to do it within the current language, but it's >> constrained by >> >> * the problem of aliasing, i.e. correctness violation, and >> >> * depending on the solution, a combinatorial explosion, and that >> >> * depending on the solution, the linker must support the scheme. > > Thanks for the analysis Alf. On the surface it looks like the > separate compilation model sinks attempts to provide input value > optimization as Dave Abrahams mentioned. But perhaps a really robust > compiler/linker would be able to do it? > > Here's what I've been thinking. Let's start with something simple: > > - Only apply optimization to classes with non trivial constructors > or POD classes of a certain size. To avoid pessimizations. > > - Restrict candidates for this optimization to parameters > that are only copied inside the function. > > - If the parameter is passed by reference to any function (including > constructors) it cannot be optimized. Perhaps we may want to > allow const references for an aggressive optimization strategy but > this would not be strictly correct/conforming. > > - The goal is to guarantee that when the function exits that the > parameter will not be modified if it is passed by reference. > Therefore we can create a single optimized version of the function > call and avoid the combinatorial explosion. Alf? > Rgat last is not sufficient in multi-threaded/concurrent code. You must also guarantee that the bject passed be reference is not modified elsewhere during the execution of the function. I think that is a more demanding requirement these days and would effectively require the implementation to do much more analysis before optimising. Note that an implementation can already do what you ask under the 'as if' rule as long as it can demonstrate that the choice cannot be detected by the program behaviour (other than efficiency) Francis -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
On Tuesday, April 10, 2012 11:14:08 AM UTC-7, Alf P. Steinbach wrote:
> Here is an example of aliasing at work: Nice example. That code would break my naive solution as I would have assumed that the input parameter wouldn't be changing. So are you saying that the compiler could detect a possible problem with this parameter seeing how it is global (or maybe a passed in reference) and choose not to optimize it at the call site? > Consider then that this binary choice is present for each sufficiently > large argument where the optimization is relevant, and so that with n > such arguments we're talking about 2^n implementation variants: a > /combinatorial explosion/ akin to the one for perfect forwarding. So you are saying: - Sometimes you will want to apply the optimization and sometimes not for various parameters of the same function. - We can implement this by defining N variations of the function and link in the one we need at link time. I can see how it may be implemented this way. However, I am not entirely clear how the callee flags implementation will work. Do you mean that the function signature will use const references for the optimized types and if the flag says pass by value then an internal copy is made? Are you suggesting putting the copy on the heap or stack? How do you access the parameter/copy within the function? Use an internal pointer? (Will cost an indirection for pass by value parameters) Checking flag before access? (Will cost for flag check) -- Ross MacGregor [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
On Thursday, April 12, 2012 12:42:23 PM UTC-7, Alf P. Steinbach wrote:
> But with a little help from the programmer, which is what > many of the keywords in C++ are about, very little remains as > impossible. E.g. in this case the help could take the form of some > attribute which, if present, prevents the optimization, e.g. because > there's a call to a function that modifies in spite of promising not > to. In regard to extending the language: what if we annotate class declarations? We could specify if a class or struct should be passed by value or by reference as the default behavior of the function call optimizer. Then the developer will be in complete control of the behavior. We will be able to say a class should always be passed by value even if it were to contain a non-trivial constructor! A very simple implementation might even forgo the alias checking and just rely on these types to never be aliased. Probably not a recommended way to go but it would be an option. Likewise, a very simple implementation could even skip the test inside the function to determine eligibility for optimization. Parameters of pass-by-reference types will always be passed by reference. Programmer beware. This could also be a way to ensure no old code breaks as only newly declared classes would participate in the function call optimization. It would be nice if we could expand the type system a bit so that the following typedef would work: typedef std::string by_ref rstring; // Old C++ void Widget::set_name(std::string const & name) { this->name = name; } // Hand optimized free C++ void Widget::set_name(rstring name) { this->name = name; } // Hand optimized free C++ void set_name(rstring name) { name += ".xml"; // error: rsting is a constant type this->name = name; } // Hand optimized free C++ void set_name(rstring name) { rsting new_name(name); new_name += ".xml"; this->name = name; } -- Ross MacGregor [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
>
> In regard to extending the language: what if we annotate > class declarations? > > typedef std::string by_ref rstring; > > // Old C++ > void Widget::set_name(std::string const & name) > { > this->name = name; > > } > > // Hand optimized free C++ > void Widget::set_name(rstring name) > { > this->name = name; > > } > > // Hand optimized free C++ > void set_name(rstring name) > { > name += ".xml"; // error: rsting is a constant type > this->name = name; > > } > > // Hand optimized free C++ > void set_name(rstring name) > { > rsting new_name(name); > new_name += ".xml"; > this->name = name; > > } The explicit copy of arguments (when required) is the trademark of Fortran (even 2003), since ALL arguments are passed by reference. C++ approach requires more discipline but In addition, the in / out /inout safeguards are also in the intent() identification of arguments. So it is either: 1) All (non-constant) references with explicit deep copies inside the functions. Safeguards may be used: Fortran 95-2003 2) A variety of argument possibilities (in the end, pointers and values). Copies are synthesized: C++ 3) Possibly a VM to deal with more intricate situations. .NET Wasn't B. Stroustrup intimately familiar with Fortran? I am also (cf. SIMPLAS, SIMPLASMPC, etc) and prefer, for reasons of coding efficiency, the C++ way. I never understood the need for C#, as it seems less productive and more baroque than C++. In my perspective, this was a difficulty of former Languages definitely solved by the C++ syntax. There are still some lacunae in C++ (I wonder why STATIC reflexion is not in C++11 as any sufficiently complex program would benefit from it sooner or later), but argument passing is not one (for mature programmers.) -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
on Fri Apr 13 2012, Jonathan Thornburg <clcppm-poster-AT-this.is.invalid> wrote:
> "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> suggested: >> Ideally there should >> be "in", "out" and "in-out" designators as in Ada > > James K. Lowden <jklowden@speakeasy.net> wrote: >> We almost have that now, right? >> >> in: value or const reference >> out: reference >> in/out: reference >> >> The last doesn't deserve distinction. It's rarely used because it's >> rarely justified. > > I have two objections to this last comment. The first is practical: > I'm willing to take James Lowden's word that in/out arguments are rarely > used in the code bases he usually sees, but I'm not at all convinced > that that's true for all C++ code bases. IMO it's wrong on its face. Every mutating member function uses an in/out argument. I know, someone will object because "this" is a pointer. PotAYto potAHto. It could just as easily have been a reference, and many think it should have been. I think it probably /is/ rare to have more than 1 in/out argument. That's a good thing, since mutations are a source of complexity. But there are notable exceptions, e.g. swap > For example, in my experience the following sorts of code all make > frequent use of in/out arguments: <snip a list> > There's a pattern here: all of these computations involve in-place updates > to some data which we don't want to copy > [possible reasons might include > * copying would be horribly slow > * there's not enough memory in the machine to make the copy > * lots of people hold (and need to keep accessing through) > pointers to the data] > > Admittedly, it's really hard (maybe impossible?) to make such computations > exception-safe. Not at all. Always remember (and don't ever forget): "exception safety" == basic_guarantee "exception safety" != strong_guarantee --Dave -- Dave Abrahams BoostPro Computing http://www.boostpro.com [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
On Fri, 13 Apr 2012 11:55:16 -0700 (PDT)
Jonathan Thornburg <clcppm-poster@this.is.invalid> wrote: > "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com> suggested: > > Ideally there should > > be "in", "out" and "in-out" designators as in Ada > > James K. Lowden <jklowden@speakeasy.net> wrote: > > We almost have that now, right? > > > > in: value or const reference > > out: reference > > in/out: reference > > > > The last doesn't deserve distinction. It's rarely used because it's > > rarely justified. > > I have two objections to this last comment. The first is practical: > I'm willing to take James Lowden's word that in/out arguments are > rarely used in the code bases he usually sees, but I'm not at all > convinced that that's true for all C++ code bases. I welcome the discussion, Jonthan. I hope to learn something from it. I didn't cite code I usually see. I specifically cited the STL because as far as I'm concerned that's the canonical example library. And I didn't say it's never justified, only rarely. The best-justified case is when the memory in question *can't* be copied usefully because it's unique. Hardware devices e.g. video buffers have the property. I have experience with some of your examples: > For example, in my experience the following sorts of code all make > frequent use of in/out arguments: > * databases (the database is implicitly an in/out argument to lots of > code) I've written several database interface libraries. I can't think of one in/out parameter other than those specified as such by stored procedures. If by "the database" you mean the connection handle or similar, those things don't need to be in the interface. > * matrix computations like LU decomposition or the singular value > decomposition (which almost always involve in-place modification of > matrices) I wrote a matrix library 15 years ago, and later adopted BLAS. Yes, there are some tricks needed to make operators convenient to use while avoiding temporaries, but IIRC very few i/o parameters. Not *none*, just not that many. > * sorting algorithms, starting with the classical CS200 > template <typename T> void sort(std::vector<T>& v); http://www.cplusplus.com/reference/algorithm/sort/ I don't consider iterators to an unpassed container to be i/o parameters. I think it's notable, actually, that the STL does not provide sort<vector<T>>. Dave Abrahams suggests that the this pointer in e.g. list::sort() can be thought of as an i/o parameter in some sense. Down that road lies a long computer science debate that I didn't mean to engage. Please note, my basic point was that i/o parameters are rare enough, on the evidence, that they don't merit distinction in the language syntax. I would guess parameters are declared 90% by value, 9% output, and 1% i/o. That's what I meant by "rarely used". By "rarely justified", I meant they are to be avoided where possible. Here's a bad example: void sqrt( double& value ); I've worked with people who believed passionately that's more efficient. You don't allocate two or three doubles on the stack; you just use the one you've got! I don't have to explain to you why that's wrong; I'm just illustrating that i/o parameters can be used more often than they are. Any function whose return type is the same as one of its input parameters is a candidate. To Just Say No unless it's really needed is the mark of careful work. > The fact that a given C++ feature is only used > rarely in your code, and/or that it's "dangerous" in some way, ..... in that it introduces complexity ... > doesn't mean that it's not valuable to have in the language Yes, of course, absolutely. --jkl -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
On 4/14/12 7:35 AM, James K. Lowden wrote:
> I didn't cite code I usually see. I specifically cited the STL > because as far as I'm concerned that's the canonical example > library. And I didn't say it's never justified, only rarely. I regard the following calls to have in/out parameters and to not be exotic corner cases of STL: std::cin << 1; std::set<int> s; s.insert(1); ++i; > Dave Abrahams suggests that the this pointer in e.g. list::sort() > can be thought of as an i/o parameter in some sense. Down that road > lies a long computer science debate that I didn't mean to engage. But both semantically and from an optimization point of view, his argument makes perfect sense. Pointer/reference is just an implementation detail when it comes to “this,” and the syntactic sugar of not having to provide this pointer/reference as an argument to the function makes code more readable, but does not really change anything beyond that. > Please note, my basic point was that i/o parameters are rare enough, > on the evidence, that they don't merit distinction in the language > syntax. I would guess parameters are declared 90% by value, 9% > output, and 1% i/o. That's what I meant by "rarely used". Since I don't follow the way you are counting, it is not surprising that my guesstimates are very different from yours. To me, modifying operations take in/out parameters – the jury is still out on assignment operators, but I believe those will be found guilty as well. Not that there's anything wrong with having in/out parameters. Actually, I'd see “output” parameters as a special case of in/out parameters (which are denoted by non-const reference in C++) where the input value is ignored – unless you can show me a valid way to have a reference parameter which only gets constructed inside the function called. Christopher -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
On Sat, 14 Apr 2012 11:29:31 -0700 (PDT)
Christopher Creutzig <christopher@creutzig.de> wrote: > > Dave Abrahams suggests that the this pointer in e.g. list::sort() > > can be thought of as an i/o parameter in some sense. Down that road > > lies a long computer science debate that I didn't mean to engage. > > But both semantically and from an optimization point of view, his > argument makes perfect sense. Pointer/reference is just an > implementation detail when it comes to â??this,â?_ and the syntactic > sugar of not having to provide this pointer/reference as an argument > to the function makes code more readable, but does not really change > anything beyond that. Recall the OP suggested parameters should have syntax denoting their i/o status (as apparently Ada has). Things like list::sort() and ++i have no parameters. Would you suggest adding syntax to them? I'm not saying anything about optimization, which doesn't interest me. Far more than better compilers, faster execution derives from better programs, which come from better programmers, who express their logic to themselves and to other programmers in source code. The language serves express logic to the humans first, and to the computer only secondarily. Were that not true, C++ would never have been invented; we would all be programming in machine code. Your point IIUC is that things like list::sort looks something like qsort(3) under the hood. That's not relevant to the question of i/o parameters per se. My point was and is that C++ syntax makes the use of i/o parameters unnecessary in most cases. > > I would guess parameters are declared 90% by value, 9% > > output, and 1% i/o. That's what I meant by "rarely used". > > Since I don't follow the way you are counting, it is not surprising > that my guesstimates are very different from yours. To me, modifying > operations take in/out parameters â?? the jury is still out on > assignment operators, (Your message arrives at my computer with a header indicating it's encoded as Windows-1252, but iconv(1) recognizes it as utf-8.) Well, clearly if we're talking about different things, we're apt to have different conclusions about them. I didn't intend to get into the theory because it's not my strength, but since you raise the subject of assignment, it seems I have to explain where I'm coming from for you to make sense of what I'm saying. As you may know, there's a body of CS research that says assignment and functions are fundamental, and that side-effects interfere with both cognition and optimization. I haven't studied it in detail, but I know in my own experience -- and, I bet, you in yours -- programs are easier to understand when the data move basically right-to-left. (I hold operator>> in std::iostreams as an exception that doesn't contradict the rule of single output. Maybe Stroustrup's disk was located somewhere to the left of the caps lock key.) Functions that have two effects -- a result on the left and an output among the parameters -- are harder to reason about. Certainly to the extent we embrace centuries of algebraic notation, we're bound to reap its benefits. The trend in computer languages is also toward functions and away from pass-by-reference. In Fortran everything was by reference. Classically: SUBROUTINE F ( I ) I = I + 1 END CALL F(3) 3 is now 4. By contrast, the functional approach has brought us languages like Haskell, in which all calls are by value and even variables don't vary. Aside: if lazy evaluation were added to C++, could Haskell programs be written purely in terms of C++ constructors? I never said i/o parameters can't be used or should be prohibited by the language. I said they are rarely used and, with the exception of low-level buffer manipulation, rarely justified. I hope my meaning and reasoning are now clear. --jkl -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
on Sat Apr 14 2012, "James K. Lowden" <jklowden-AT-speakeasy.net> wrote:
> I don't consider iterators to an unpassed container to > be i/o parameters. I think it's notable, actually, that the STL does > not provide sort<vector<T>>. What in particular do you think is notable about it? There are actually some very good arguments that the STL should have provided that interface, and the committee is actively considering adding it. > Dave Abrahams suggests that the this pointer in e.g. list::sort() can > be thought of as an i/o parameter in some sense. Down that road lies a > long computer science debate that I didn't mean to engage. I don't see that there's anything much debatable here. Fundamentally the computation that sorts a list is the same thing whether you spell it l.sort() (yes, the STL does provide that one) or sort(l). The underlying machine model against which we program in C++ includes mutation of state at a fundamental level. If you want to live in a world where you can dismiss mutation of state as rare, you should be programming in Haskell. (**) -Dave (**) Which isn't to say you're unwelcome here in C++-land, of course :-) -- Dave Abrahams BoostPro Computing http://www.boostpro.com [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|
![]() |
| Thread Tools | |
| Display Modes | |
|
|