take following class and two object definitions:

class Rect{
     public:
      enum centimeter;
      enum meter;
      Rect(double len,double wid,enum centimeter){
       length=(len/100);
       width=(wid/100);
      }
      Rect(int len,int wid,enum meter){
       length=len;
       width=wid;
      }
      //rest of implementation
     private:
      double length;//in meters
      double width;//in meters
    };
    Rect obj1(10,5,Rect::centimeter());
    Rect obj2(10,5,Rect::meter());

two previous constructors have dummy enum parameters to solve calling ambiguity caused in case these dummy parameters didn't exist. Now in spite of possibility of using named constructors here, if I insist on using these dummy parameters, does this violate any coding rule that I should be aware of ?

Recommended Answers

All 25 Replies

I see nothing wrong with that. Maybe if you are going to use this trick a lot, either you may want to have global (or namespaced) identifiers (enums) for your units or you may want to attach the unit to the type of the parameters, this way you don't have to implement a constructor for every different unit by using implicit type casts that convert a centimeter to a meter value or whatever.

If you are concerned with dimensions (or units) and you are comfortable with template meta-programming, you may want to look at this tutorial or this library.

mike_2000_17

either you may want to have global (or namespaced) identifiers (enums) for your units or you may want to attach the unit to the type of the parameters, this way you don't have to implement a constructor for every different unit by using implicit type casts that convert a centimeter to a meter value or whatever.

can you give some samples for clarification ?

Ok.. well I won't do the global identifiers idea, that's just like your code but you take the enums out of the class declaration. But to attach the unit to the type is not obvious at first, but quite straight-forward:

struct CentimeterValue; //forward declaration.

struct MeterValue {
  double value;
  explicit MeterValue(double aValue) : value(aValue) { }; //"explicit" is crucial here.
  MeterValue(const CentimeterValue& aValue) : value(aValue.value / 100.0) { }; //this would actually need to be defined later, but just for example.
};

struct CentimeterValue {
  double value;
  explicit CentimeterValue(double aValue) : value(aValue) { };
  CentimeterValue(const MeterValue& aValue) : value(aValue.value * 100.0) { };
};

But again, the tutorial I mentioned in the previous thread is the most robust way to accomplish this, read it to know why. One main reason is that if you have many different unit you will have to program A LOT of pseudo-copy-constructors and unit transformations.

Also, operator overloading can make these structs act like primitive type.

mike_2000_17

struct CentimeterValue; //forward declaration.

struct MeterValue {
  double value;
  explicit MeterValue(double aValue) : value(aValue) { }; //"explicit" is crucial here.
  MeterValue(const CentimeterValue& aValue) : value(aValue.value / 100.0) { }; //this would actually need to be defined later, but just for example.
};

struct CentimeterValue {
  double value;
  explicit CentimeterValue(double aValue) : value(aValue) { };
  CentimeterValue(const MeterValue& aValue) : value(aValue.value * 100.0) { };
};

To get into more details I think some tiny changes to your classes wouldn't be bad:

struct CentimeterValue; //forward declaration.

struct MeterValue {
  double value;
  inline explicit MeterValue(const double & aValue) : value(aValue) { }; //"explicit" is crucial here.
  inline MeterValue(const CentimeterValue& aValue) : value(aValue.value / 100.0) { }; //this would actually need to be defined later, but just for example.
};

struct CentimeterValue {
  double value;
  inline explicit CentimeterValue(const double & aValue) : value(aValue) { };
  inline CentimeterValue(const MeterValue& aValue) : value(aValue.value * 100.0) { };
};

Umm, adding the "inline" keyword there has absolutely no effect. Read about inline functions. Any method whose implementation is put in the class declaration is by default _suggested_ to be inlined, and adding the inline keyword simply does nothing, so don't waste your finger muscles on adding inline keywords everywhere. Only global or namespace scope free-functions or methods (static or not) whose implementation is later in the header file can be _suggested_ to the compiler to be inlined via the inline keyword.

mike_2000_17

Umm, adding the "inline" keyword there has absolutely no effect. Read about inline functions. Any method whose implementation is put in the class declaration is by default _suggested_ to be inlined, and adding the inline keyword simply does nothing, so don't waste your finger muscles on adding inline keywords everywhere. Only global or namespace scope free-functions or methods (static or not) whose implementation is later in the header file can be _suggested_ to the compiler to be inlined via the inline keyword.

but changing parameter of first constructors of classes to const reference to double, still cool!!!:P

I have an eagle eye for code, but not that sharp. "const double&" is OK, kind of useless, but OK.

mike_2000_17

I have an eagle eye for code, but not that sharp. "const double&" is OK, kind of useless, but OK.

I don't think avoiding an extra useless copy of a double would be useless!!!:)

I would do this

#include <iostream>

class Rect{
        public:
                enum UNIT {CENTIMETER,METER};
                Rect(double len,double wid,UNIT unit){
                        if(unit == CENTIMETER){
                                length=(len/100);
                                width=(wid/100);
                        }
                        else{
                                length=len;
                                width=wid;
                        }
                        std::cout << length << ":" << width << std::endl;
                }
        //rest of implementation
        private:
                double length;//in meters
                double width;//in meters
};      

int main(){

        Rect obj1(100,100,Rect::CENTIMETER);
        Rect obj2(10,5,Rect::METER);
}

I don't think avoiding an extra useless copy of a double would be useless!!!

Well, I thought you originally made a _joke_ code correction altogether, because you have been posting questions on micro-optimization. It led me to believe you knew the inline keyword was useless there and the "const double&" is not way better than just "double", because swapping a _theoretical_ copy of a double by a _theoretical_ reference to a double (which is implemented as a pointer) is the same in theory. In practice, most compilers, even without optimization options, will not create a useless copy of the double or a useless pointer (ref), so it doesn't make a difference. Furthermore, while doing floating-point operations, it's more efficient to a stick to floats (or doubles) by value and avoid pointers or too many integers, it's a matter of avoiding mixing too much floating-point registers and integer registers.

mike_2000_17

Well, I thought you originally made a _joke_ code correction altogether, because you have been posting questions on micro-optimization. It led me to believe you knew the inline keyword was useless there and the "const double&" is not way better than just "double", because swapping a _theoretical_ copy of a double by a _theoretical_ reference to a double (which is implemented as a pointer) is the same in theory. In practice, most compilers, even without optimization options, will not create a useless copy of the double or a useless pointer (ref), so it doesn't make a difference. Furthermore, while doing floating-point operations, it's more efficient to a stick to floats (or doubles) by value and avoid pointers or too many integers, it's a matter of avoiding mixing too much floating-point registers and integer registers.

You know as far as I know this is not a place for jokes...
So with you disappointing me you say, the fact that reference parameter of an inline function is not a pointer, is a joke; good luck with that and even if it was one I think not involving extra copies by first constructors of your classes is a nicer one because what you say would be true in case parameters were const double.

Well just to settle that little argument about passing a double by value or const-ref, let me use the words of two of the gods of C++, Andrei Alexandrescu and Herb Sutter:

Prefer to follow these guidelines for choosing how to take parameters. For input-only parameters:
- Always const-qualify all pointers or references to input-only parameters.
- Prefer taking inputs of primitive types (e.g., char, float) and value objects that are cheap to copy (e.g., Point, complex<float>) by value.
- Prefer taking inputs of other user-defined types by reference to const.
- Consider pass-by-value instead of reference if the function requires a copy of its argument. This is conceptually identical to taking a reference to const plus doing a copy, and it can help compiler to better optimize away temporaries.

The second and last points here were the two points I was making in my previous post.

Why do you need centimeter and meter, you can just work with meter. Centi is just an
extension of meter? And come to think of it, your just scaling the width and the height. It makes no sense to me, to put that in the constructor. It should go as a
member function. This class is essentially the same as yours :

class Rectangle{
private:
	unsigned width;
	unsigned height;
public:
	Rectangle(unsigned w, unsigned h)
		: width(w), height(h) {}
	
	void scaleWidth(float s){
		width = (unsigned)ceil( width * s);
	}
	void scaleHeight(float s){
		height = (unsigned) ceil( width * s);
	}
	void scale(float s){
		scaleWidth(s); 
		scaleHeight(s);
	}
};

mike_2000_17

Well just to settle that little argument about passing a double by value or const-ref, let me use the words of two of the gods of C++, Andrei Alexandrescu and Herb Sutter:
Prefer to follow these guidelines for choosing how to take parameters. For input-only parameters:
- Always const-qualify all pointers or references to input-only parameters.
- Prefer taking inputs of primitive types (e.g., char, float) and value objects that are cheap to copy (e.g., Point, complex<float>) by value.
- Prefer taking inputs of other user-defined types by reference to const.
- Consider pass-by-value instead of reference if the function requires a copy of its argument. This is conceptually identical to taking a reference to const plus doing a copy, and it can help compiler to better optimize away temporaries.

The second and last points here were the two points I was making in my previous post.

still no conflict with my idea because what we're dealing with here are inline methods, and points listed have not considered that special case. Generally in case of inline functions, if we have a parameter with a declaration as "someFunction(const DataType)" or "someFunction(const DataType &)" or "someFunction(DataType &)", no copying or address taking is involved and arguments are used directly and of course in any case(execept for possible optimizations related to end of usage lifetime of input argument in the caller function that I don't get into that) if you have your parameter declared as "someFunction(DataType)" you're telling that I might change value of parameter during function execution, so a copy of input argument should be made.

>> you're telling that I might change value of parameter during function execution, so a copy of input argument should be made.

Sure, it says that in _theory_, but, sending as "DataType", the copy is created as temporary and optimized away easily by 99.9% of compilers. If sent as "const DataType", no copy is made but it also can no longer be used as a variable inside the function, so the end-result is the same but more restrictive in the coding (but this can be an advantage for const-correctness, that I agree fully with!). If sent as "const DataType&" or "DataType&", a temporary pointer is sent (which is as expensive or more expensive, in this case, than sending a temporary double variable) and the variable is used by reference everywhere in the function, an extra layer of indirection in the temporary that compilers will find more difficult to optimize away. For _inline_ functions, it's even easier for the compiler to optimize so all of the above really boil down to the same executable code, but if not inlined, passing a double by value is better so either
- "const double" if you don't want to use the parameter as a variable inside the function, but I would say 98% or so of the time it's exactly the same as "double";
- "double&" if it has to be changed, i.e., as an input and output variable;
- "double" in general for all normal input parameters.
- "const double&" is just useless, but not "const DataType&" in general if DataType is not cheap to copy (if it can all fit on the registers, like a small size vector of doubles (like 2D or 3D vector), it's cheap to copy and worth avoiding indirection in the function).

You said: "..(const DataType)...(const DataType &)...(DataType&), no copying or address taking is involved and arguments are used directly...". What about this:

class someThreadClass {
  private:
    ...
    const bool& terminateSignal;
  public:
    someThreadClass(const bool& aTerminateSignal) : terminateSignal(aTerminateSignal) { };
};

You say no address taking is involved in passing the "const bool&" parameter? That's wrong. Address taking and passing a temporary pointer is involved by default, it possibly will be optimized away but that's later and not a easy and guaranteed as if it was passed by value (not in this case because it needs the indirection and thus, the addressing will not be optimized away in this case, but in the original case, passing by-value will have a better chance to be optimized by the compiler). In other words, it's easier for a compiler to figure out that "B = A; C = B;" is the same as "C = A;" if B does not appear any where else (temporary value) than to figure out that "B = &A; C = *B;" is the same as "C = A;", just because of an extra layer of indirection.

mike_2000_17

>> you're telling that I might change value of parameter during function execution, so a copy of input argument should be made.

Sure, it says that in _theory_, but, sending as "DataType", the copy is created as temporary and optimized away easily by 99.9% of compilers.

First How do you get these statistical numbers?
When this temporary has been created, what does optimization mean anymore ?

If sent as "const DataType", no copy is made but it also can no longer be used as a variable inside the function, so the end-result is the same but more restrictive in the coding (but this can be an advantage for const-correctness, that I agree fully with!).

I still disagree with end-results being the same.

If sent as "const DataType&" or "DataType&", a temporary pointer is sent (which is as expensive or more expensive, in this case, than sending a temporary double variable) and the variable is used by reference everywhere in the function, an extra layer of indirection in the temporary that compilers will find more difficult to optimize away.

I stress again that reference parameters for inline functions aren't equivalent to pointers(do you know philosophy of reference existence ?).

For _inline_ functions, it's even easier for the compiler to optimize so all of the above really boil down to the same executable code, but if not inlined, passing a double by value is better so either

I agree that in case of not-inlined functions, reference parameters shouldn't be used for primitive types.

- "const double" if you don't want to use the parameter as a variable inside the function, but I would say 98% or so of the time it's exactly the same as "double";

Not the same when function is not inlined.

- "double&" if it has to be changed, i.e., as an input and output variable;
- "double" in general for all normal input parameters.
- "const double&" is just useless, but not "const DataType&" in general if DataType is not cheap to copy (if it can all fit on the registers, like a small size vector of doubles (like 2D or 3D vector), it's cheap to copy and worth avoiding indirection in the function).

I stress again: I've focused on inline functions...

You said: "..(const DataType)...(const DataType &)...(DataType&), no copying or address taking is involved and arguments are used directly...". What about this:

class someThreadClass {
  private:
    ...
    const bool& terminateSignal;
  public:
    someThreadClass(const bool& aTerminateSignal) : terminateSignal(aTerminateSignal) { };
};

You say no address taking is involved in passing the "const bool&" parameter? That's wrong. Address taking and passing a temporary pointer is involved by default, it possibly will be optimized away but that's later and not a easy and guaranteed as if it was passed by value (not in this case because it needs the indirection and thus, the addressing will not be optimized away in this case, but in the original case, passing by-value will have a better chance to be optimized by the compiler)

.
Why do you think possible address taking by terminateSignal has anything to do with how argument will be treated for constructor call (do you know the possible difference between references declared in the body of a function or declared as data member of a class or struct, with those declared as parameter of function)?

In other words, it's easier for a compiler to figure out that "B = A; C = B;" is the same as "C = A;" if B does not appear any where else (temporary value) than to figure out that "B = &A; C = *B;" is the same as "C = A;", just because of an extra layer of indirection.

What usage does this sentence have ?

Agni

I would do this

#include <iostream>

class Rect{
        public:
                enum UNIT {CENTIMETER,METER};
                Rect(double len,double wid,UNIT unit){
                        if(unit == CENTIMETER){
                                length=(len/100);
                                width=(wid/100);
                        }
                        else{
                                length=len;
                                width=wid;
                        }
                        std::cout << length << ":" << width << std::endl;
                }
        //rest of implementation
        private:
                double length;//in meters
                double width;//in meters
};      

int main(){

        Rect obj1(100,100,Rect::CENTIMETER);
        Rect obj2(10,5,Rect::METER);
}

Actually there would be some ways, but my point is to understand using dummy enum parameters to distinguish constructor calls is necessarily a sign of bad code design or not.

firstPerson

Why do you need centimeter and meter, you can just work with meter. Centi is just an
extension of meter? And come to think of it, your just scaling the width and the height. It makes no sense to me, to put that in the constructor. It should go as a
member function. This class is essentially the same as yours :

class Rectangle{
private:
	unsigned width;
	unsigned height;
public:
	Rectangle(unsigned w, unsigned h)
		: width(w), height(h) {}
	
	void scaleWidth(float s){
		width = (unsigned)ceil( width * s);
	}
	void scaleHeight(float s){
		height = (unsigned) ceil( width * s);
	}
	void scale(float s){
		scaleWidth(s); 
		scaleHeight(s);
	}
};

Actually there would be some ways, but my point is to understand using dummy enum parameters to distinguish constructor calls is necessarily a sign of bad code design or not.

Well maybe you know something you don't want to tell me... but for all I know, and I do acknowledge that I could be wrong but I have never read anything to the contrary, the process of compilation of an inline function is something like this. Say you have a simple setter function inlined (assuming the compiler _chooses_ to inline it, which it probably will):

class A {
  private:
    double value;
  public:
    void setByValue(double aValue) { value = aValue; };
    void setByRef(const double& aValue) { value = aValue; };
};
int main() {
  A object;
  double param = 10.0;
  object.setByValue(param);
  object.setByRef(param);
};

The compiler will not, at first, assume _anything_ about the usage of the passed parameter, because it cannot, a compiler is not a human who can look at this right away and know what's best. So it expands to something like this:

int main() {
  A object;
  double param = 10.0; 
  //inlines the setByValue:
  {
  double aValue = param; //copies to temporary "aValue"
  object.value = aValue;      //sets the data member
  };
  //inlines the setByRef:
  {
  const double& aValue = param; //takes address (or ref) to param
  object.value = aValue;
  };
};

Once this is laid out, it will look at it and do a DU-chain optimization to clean useless temporaries:

int main() {
  A object;
  double param = 10.0; 
  //inlines the setByValue:
  {
  object.value = param; //just sets value directly, no addressing and no copying.
  };
  //inlines the setByRef:
  {
  object.value = param; //just sets value directly, no addressing and no copying.
  };
};

So now you can see the purpose of that last sentence in my previous post. And obviously, in this case it makes no difference, but in some more complex cases the extra level of indirection will make the above style of optimization a little bit harder, but I can't think of a complex example where it would make a big difference (that's why my _approximate_ _subjective_ "statistics" are so close to 100%).

Even further optimizing for this case, it will end up with just this at the end (in the POD sense of a constructor):

int main() {
  A object(10.0);
};

You have to understand this is the only way a compiler can attack the code. It has to start in a very verbose way, i.e. translating exactly the written code without any assumptions, then it starts reducing the code size and speed, cleaning temporaries, without changing the behaviour.

At least, in my humble mind, this is how I understand it and I have no evidence to the contrary, do you? I would really like to know if I'm wrong or if there's a distinction for constructors. Do you have any sources to read on that?

mike_2000_17

Well maybe you know something you don't want to tell me... but for all I know, and I do acknowledge that I could be wrong but I have never read anything to the contrary, the process of compilation of an inline function is something like this.

I'm not a compiler expert, are you? If not neither I nor you can't be sure about compiling procedure, but not that I'm not sure about what I've told so far because I've read about them and haven't used my imagination.

Say you have a simple setter function inlined (assuming the compiler _chooses_ to inline it, which it probably will):

class A {
  private:
    double value;
  public:
    void setByValue(double aValue) { value = aValue; };
    void setByRef(const double& aValue) { value = aValue; };
};
int main() {
  A object;
  double param = 10.0;
  object.setByValue(param);
  object.setByRef(param);
};

The compiler will not, at first, assume _anything_ about the usage of the passed parameter, because it cannot, a compiler is not a human who can look at this right away and know what's best.

I'm not sure about quantity of compiler intelligence, are you?.

So it expands to something like this:

int main() {
  A object;
  double param = 10.0; 
  //inlines the setByValue:
  {
  double aValue = param; //copies to temporary "aValue"
  object.value = aValue;      //sets the data member
  };
  //inlines the setByRef:
  {
  const double& aValue = param; //takes address (or ref) to param
  object.value = aValue;
  };
};

I tell again I can't be sure about that unless you're a compiler expert and better than me in this field but I don't think setByRef function will take a temporary reference so that it'll need to be optimized away.

Once this is laid out, it will look at it and do a DU-chain optimization to clean useless temporaries:

int main() {
  A object;
  double param = 10.0; 
  //inlines the setByValue:
  {
  object.value = param; //just sets value directly, no addressing and no copying.
  };
  //inlines the setByRef:
  {
  object.value = param; //just sets value directly, no addressing and no copying.
  };
};

So now you can see the purpose of that last sentence in my previous post.
And obviously, in this case it makes no difference, but in some more complex cases the extra level of indirection will make the above style of optimization a little bit harder, but I can't think of a complex example where it would make a big difference (that's why my _approximate_ _subjective_ "statistics" are so close to 100%).

I don't have any info on DU-chain optimization so don't know if compiling of setByValue is really that way, but I tell again that I don't think compiling of setByRef is like that with taking an extra temporary reference.

Even further optimizing for this case, it will end up with just this at the end (in the POD sense of a constructor):

int main() {
  A object(10.0);
};

You have to understand this is the only way a compiler can attack the code. It has to start in a very verbose way, i.e. translating exactly the written code without any assumptions, then it starts reducing the code size and speed, cleaning temporaries, without changing the behaviour.

I can't be sure about compiler intelligence cause I don't have any detailed info on that subject.

At least, in my humble mind, this is how I understand it and I have no evidence to the contrary, do you? I would really like to know if I'm wrong or if there's a distinction for constructors. Do you have any sources to read on that?

I've read about all I've told, I'll try to tell you where to go and take a look, later.

I found this thread to be explaining quite clearly what I tried to explain, nobody seems to disagree with me either. I have looked around the web about this, but to no avail, nobody seems to point out any difference of pass-by-value or pass-by-reference guidelines when it comes to inline functions, methods, or constructors.

Hey guys, you are getting way off topic. You guys made this thread your own.
Plus, in this debate, I would have to agree with mike_2000_17. There is no, if hardly any performance difference between a reference versus non-reference when it comes to primitive types. If you disagree then do a performance check.

I was just about to say something about closing this digressing argument. So Garrett, if you are not convinced, either test it or we can agree to disagree, or you can start another thread on that to see what other people think.

But I enjoyed the debate! :)

firstPerson

Hey guys, you are getting way off topic. You guys made this thread your own.

Yeah but you know the challenge was damn hot !!!

mike_2000_17

I was just about to say something about closing this digressing argument. So Garrett, if you are not convinced, either test it or we can agree to disagree, or you can start another thread on that to see what other people think.

But I enjoyed the debate! :)

Man what a debate it was !!!
I may still post good source addresses to go and take a look about what I've told in the future.
Who knows, maybe we should start a new thread for such hot subject!!!:)

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.