implicit conversion at call by value and polymorphism - c++

Consider the following scenario:
Some polymorphic classes:
struct iClass
{
virtual ~iClass(){}
};
struct CommonClass : public iClass
{
char str[128];
CommonClass() { ... }
...
};
struct SpecificClass : public iClass
{
char str[32];
SpecificClass () { ... }
SpecificClass (SpecificClass& src) { strcpy(...); ... }
SpecificClass (CommonClass& src) { strcpy(...); ... }
SpecificClass (const CommonClass& src) { strcpy(...); ... }
void foo() { ... }
};
Furthermore a function:
void someFunc(SpecificClass sc) { sc.foo(); } // pass by value: i want it copied!
int main ()
{
CommonClass comCl;
someFunc(comCl); // <- Error: no matching function for call to 'SpecificClass::SpecificClass(SpecificClass)' NOTE: no &
SpecificClass specCl(comCl);
someFunc(specCl); // Works normal, but the str gets copied double times this way, isnt it?
return 0;
}
Why does the compiler not allow the conversion from CommonClass to SpecificClass in the first function call although a new SpecificClass gets constructored on the call anyways and there is a constructor for this specific conversion?
And why do i get this strange error message? A call to a copy constructor without a reference?
Can anyone share some insight into this problematic?
Btw, i have to use gcc 4.1.2

someFunc requires an argument of type SpecificClass, and you provide one of type CommonClass. Since you have defined a conversion constructor
SpecificClass::SpecificClass(CommonClass &)
this conversion can be performed implicitly, and that's great so far.
[Note, though, that your conversion constructor takes a non-const reference, which works here because you actually supply it with an lvalue – however, in general, I'd assume that taking the argument of the conversion constructor as const CommonClass & is usually better (since the conversion from one type into another isn't usually expected to alter the original).]
However, the problem occurs in the next step: The freshly converted object of type SpecificClass now needs to be copied into the function argument sc of someFunc. For this you need a copy constructor for SpecificClass which can take an rvalue (that's what the freshly converted SpecificClass object is, because it's a temporary).
Your copy constructor
SpecificClass::SpecificClass(SpecificClass &)
is declared to take a non-const lvalue reference, which can't bind to the temporary. So you must change this into
SpecificClass::SpecificClass(const SpecificClass &)
to fix the problem. And that's the usual way of declaring a copy constructor anyway.
One other thing I can't help noticing: You have a function someFunc that is meant to be called on objects of a very specific type only. Yet you call it on a general (base class?) type. Clearly, this can be made to work in the way you described, but it is in many ways counter-intuitive and not well-aligned with the principle of object-oriented programming. It also makes me wonder how the 'specific' object is actually created from the 'common' one, i.e. what the conversion constructor actually does. Intuitively, I'd assume the 'common' object given as input lacks the 'specific' information to create a 'specific' object.
In any case, you may want to reconsider the structure of your class hierarchy and/or the purpose of someFunc. None of this is directly related to the problem described in your question, though.

Related

Automatic Object Construction in Function Parameters by Passing Constructor Parameters

Can you explain why the following code compiles and runs? What is the concept at play here, and what are the limitations/requirements for such a methodology to work?
class string_wrapper
{
public:
string_wrapper(string i_string);
string m_value;
int m_length;
};
string_wrapper::string_wrapper(string i_string)
{
m_value = i_string;
m_length = i_string.length();
}
void bar(string_wrapper i_param)
{
cout << i_param.m_value << std::endl;
}
void foo()
{
string test_string = "test1";
bar(test_string);
}
int main()
{
test_function_b();
}
Output:
test1
I would expect this code to not compile. foo() is passing a string parameter to bar(), when bar only takes a string_wrapper parameter. However, the compiler is smart enough to know that it can use the string parameter as a parameter to a constructor for a string_parameter object, which presumably is then passed on as the actual parameter to bar().
Is this behavior within the C++ standard, or unique to my compiler (Visual Studio 2017, version 15.9, in this case)? Any insight or terminology I can use for further research would be appreciated.
Whats going on here is implicit construction. You are right about the compiler being 'smart' enough to know that you can create a string_wrapper out of a std::string and so does it automatically for you.
To stop this happening you can use the explcit keyword like this:
...
explicit string_wrapper(string i_string);
...
The explicit keyword tells the compiler that you do not want it to automatically construct string_wrapper objects for you out of std::string's. This can stop hard to track down bugs because it prevents accidentally constructing objects (especially on function returns and when passing arguments).
Implicit construction can happen when an object has only one non-defaulted parameter (implicit conversion is something you should research as well). It can be a powerful tool and can allow things like proxy classes and transparent APIs (i.e std::vector's reference class). Generally you should declare single parameter (or single undefaulted value) constructors (and conversion operators) explicit unless you are making a design decision not too.

C++: how to pass a variable as an argument to class constructor

I am faced with a very puzzling issue. I am trying to construct an object using a variable as a parameter.
Have a look at this code please:
#include <iostream>
class A {
public:
A() : val(0) {};
A(int v) : val(v) {};
private:
int val;
};
main() {
int number;
A a; /* GOOD, object of type A, created without arguments */
A b(2); /* GOOD, object of type A, created with one argument */
A c(); /* BAD, C++ thinks this is declaration of
a function returning class A as a type */
A(d); /* GOOD, object of type A again; no arguments */
A(number); /* BAD, tries to re-declare "number" as object of type A */
}
I think I do understand why objects "a", "b" and "d" can be created, whereas the rest can not.
But I would really need the syntax of the last declaration, i.e.
A(number);
to create a temporary object, to pass as an argument to another object.
Any idea how to work around it?
Kind regards
Your intention is to create a temporary object, but this object would also be anonymous : you have no way to refer to it after the semicolon.
There is no point in creating an anonymous temporary object just to discard it. You have to instantiate your temporary directly at the call site of the ctor/method that will consume it.
Solution
To pass an anonymous temporary object to a function, you actually need to instantiate it inside the arguments' list :
functionExpectingA(A(number));
For the line of "c declaration", you are poking at the most vexing parse. If you actually need to pass a default constructed A object as an argument to another object constructor, you need to add another pair of braces to do the trick (so the compiler can distinguish it from a function declaration) :
class OtherClass
{
public:
OtherClass(A a)
{
//...
};
};
OtherClass obj((A()));
^ ^
EDIT #1 : jrok pointed out that the argument given to A constructor is not enough to resolve the ambiguity.
If you need to pass an anonymous temporary object that is built with an argument, there is no ambiguity (so no need for the extra parentheses), you still need the parentheses around the anonymous object construction.:
OtherClass obj((A(number)));
C heritage : a single argument is not enough
EDIT #2 : "why giving a single argument to A constructor does not resolve the ambiguity".
What happens with OtherClass obj(A(number)); ?
This is a declaration for a function named obj, taking an A object as its unique argument. This argument is named number.
i.e: It is exactly equivalent to OtherClass obj(A number);. Of course, you can omit the argument name at function declaration. So it is also sementically equivalent to OtherClass obj(A);
The syntax with parentheses around the object name is inherited from C :
int(a); // declares (and defines) a as an int.
int a; // strictly equivalent.
What with more arguments to the constructor then ?
If you added a ctor to OtherClass taking two (or more) arguments :
OtherClass(A a1, A a2)
{
//...
};
Then this syntax :
A arg1, arg2;
OtherClass obj(A(arg1, arg2));
Would this time actually declare obj as an instance of OtherClass.
This is because A(arg1, arg2) cannot be interpreted as name declaration for an A. So it is actually parsed as the construction of an anonymous A object, using the constructor with 2 parameters.

Error "recursive on all control paths" when copy constructor is used and virtual function present

The error below is confusing me. Here is a short piece of a much more complicated code. It appears strange to me, that only the existence of both a templated constructor and a virtual method cause an error, and only when copy-initializing an object.
Does anyone have an idea? Thanks.
class A
{
long *p;
public:
A():p(0)
{
}
template<class T>
A(T val):p(val)// 1
{
}
operator long*()
{
return p;
}
};
class B
{
virtual void f()// 2
{
}
};
class C : public A, public B
{
};
void main()
{
C c;
The next line in main() is
A a=c;
and this triggers the error below if both the lines marked // 1 and // 2 are present:
warning C4717: 'C::C' : recursive on all control paths, function will cause runtime stack overflow
But when the following is used in main(), there is no error:
A a;
a=c;
}
What you have is a nasty confluence of copy elision and a constructor that makes a copy of the parameter.
First, let's clear up a misunderstanding: A a = c; is not equivalent to A a; a = c;. The first calls the copy ctor, the second calls the assignment operator. See for yourself using this code sample.
The constructor A::A<T>(T) could make a copy of T whenever it is called. Unfortunately, if you call it using an A parameter (or in your example C, which is-a A), the parameter will attempt to copy itself, which calls A::A<T>(T) again, which copies itself again, and again... until stack overflow.
Why doesn't this happen when you don't have the virtual void f() in B? This is a side effect of copy elision, which is an implementation-dependent feature. Having the virtual method there might have been enough for visual studio to decide not to elide the copy, but in any case you shouldn't depend on it. This is why you are strongly advised not to have observable side-effects for copy ctors.
Just in case you were looking for a solution, you can remove the copy by changing A::A<T>(T) to take a reference, like A::A<T>(T&). Even better, take a const T& because this helps ensure that there are no side effects in the ctor (as you can't modify the T).
A a=c; // this results in A::A(C c) template constructor instantiation.
After that it is recursion since to make a copy, you need to make a copy, you need to make a copy.... :)
For proper usage refer this.

Some questions about C++ from a C# developer [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I have some questions about C++ from a C# developer.
For a few days I have been looking at some C++ code, and I have the following questions:
When do use Foo::, Foo. and Foo-> ?
When do I use a real constructor and when just String a; (sometimes I need to do something like String a("foo");)
What is the difference between these signatures: int foo(int a) and int foo(int &a)?
:: is used either to explicitly specify a namespace (std::string, for example, for the string class in the namespace std), or for static members of a class.
. is used much as in C#, to refer to a member of a class.
-> is used with pointers. If p is a pointer to an object obj, then p->x has the same meaning as obj.x.
when do i use a real constructor and when just String a; (sometimes i need to do something like String a("foo");)
When you need to. String a is roughly equivalent to C#'s a = new String() (with the caveat that if String is a non-POD type, it may contain uninitialized members.)
If you need a initialized to a specific value, you do that. (either with String a("foo"), or with String a = "foo")
where is the difference between these signatures: int foo(int a) and int foo(int &a)?
The & denotes a reference. It's not quite a C# reference, but there are similarities. In C#, you have value types and reference types, and reference types are always passed by reference.
In C++, there's no such distinction. Every type can be passed by value or by reference. The type T& is a reference to T. In other words, given the following code:
void foo(int& j);
void bar(int j);
int i = 42;
foo(i);
bar(i);
foo will get a reference to i, which means it it can modify the value of i.
bar will get a copy of i, which means that any modifications it makes will not be reflected in i.
You often use const T& (a reference to const T) as a way to avoid the copy, while still preventing the callee from modifying the object.
1: Assuming you which to call a method
Foo::theMethod(...)
is for example used when calling a static method of a class Foo
Foo.theMethod(...)
is when you have an object named Foo
Foo->theMethod(...)
is when you have a pointer to a object of named Foo
2:
String a;
calls the default constructor that takes no arguments
String a("foo")
calls a overloaded constructor
3:
int foo(int &a)
takes a reference to an integer, so within the method you are able to manipulate a.
int foo(int a)
makes a copy, manipulating it wont have any effect of the actual parameter passed in after leaving the method.
Question 1:
It depends on what Foo is. The :: operator is called the scope
resolution operator; the operand on the right must be a namespace or a
class, and the operand to the left a member of the namespace or class.
If Foo is a class, Foo:: can be used to access a static member, or
from within a member of a derived class, to access the member of the
base class: e.g.:
class Foo
{
public:
virtual void f();
static void g();
};
int h()
{
Foo::g();
}
class Derived : public Foo
{
public:
virtual void f()
{
Foo::f(); // Call function in base class...
}
}
It's often used to access namespace members as well, e.g. std::cout
(the cout object in namespace std).
The . operator is a member access operator, and requires an object (or
a reference to an object) as the left hand operand. Thus (using the
above definitions):
Foo obj;
obj.f();
void i( Foo& rFoo )
{
rFoo.f();
}
It can also be used to access static members, if you have an instance:
Foo obj;
obj.g();
The -> is very much like the . operator, except that it takes a
pointer to an instance, rather than an instance, and (very importantly)
it can be overloaded. Thus:
Foo* obj;
obj->g();
// And if Ptr is a user defined type with an overloaded
// `operator->` which returns a Foo*
Ptr obj;
obj->g();
Again, you can also use this syntax to access a static member, if you
have a pointer to an object.
Question 2:
The definition String a; calls a real constructor. You use String
a; when you want the default constructor; the one with no parameters.
You use String a( "foo" ); when you want the constructor which takes a
char const* (or a char const (&)[4], but that's highly unlikely, since it
will only work for a string literal with exactly three characters).
In general, when defining variables:
String a; // default constructor...
String a1(); // NOT what it looks like: this is a
// function declaration, and not the
// definition of a variable!!!
String b( x, y, z ); // constructor taking x, y and z as arguments...
String c = x; // implicitly convert `x` to String, then
// copy constructor.
The last form is a bit tricky, since the copy constructor may be (and
almost always is) elided, but the legality of the program is defined by
the rule above: there must be a way of implicitly converting x into a
String, and String must have an accessible copy constructor.
In other contexts, e.g. new String(), the form with empty parameters
can be used for "value construction", which is the default constructor
if there is a user defined one, otherwise zero initialization.
Question 3:
The first is pass by value, and passes a copy of the argument to the
function. The second is pass by reference, and passes a reference
(which behaves sort of like a hidden, automatically dereferenced
pointer) to the function. Thus:
void f( int a )
{
++ a; // Modifies local copy, has no effect on the argument.
}
void g( int& a )
{
++ a; // Modifies the variable passed as an argument.
}
Note that in the first case, you can pass an arbitrary expression; in
the second, you must pass something called an lvalue—that is,
something you can access afterwards using a similar expression (a named
variable, or a dererenced pointer, or an element in a named array,
etc.).
String a : construct an empty String object
String a("foo") : construct a String object initalized to "foo"
int foo(int a) : pass a by value/copy to foo. Inside foo if you modify a , a will not be impacted outside foo
int foo(int& a) : pass a by reference inside foo. If you modify a , a will also be modify once foo ended
Foo:: - Static methods
Foo. - Instance Methods when you have a stack object instance. (MyObject obj)
Foo-> - Instance methods when you have a object pointer. (MyObject* pObj = new MyObject())
Whenever you need to pass some value to the constructor.
int& is a reference to an int. Any changes to a within the method will affect a outside the method. (Equivalent to ref in C#)

"No matching function for call error" with g++

I have a class A
template<typename T>
class A : public std::auto_ptr<T>
{
typedef std::auto_ptr<T> Super;
public:
A() : Super() { }
A(T* t) : Super(t) { }
A(A<T>& o) : Super(o) { }
...
};
and IConstEnumerator
template<typename T>
struct IConstEnumerator
{
...
virtual IEnumerator<T>* GetEnumerator() = 0;
virtual IConstEnumerator<T>* GetEnumerator() const = 0;
};
When I run this code
AP< IConstEnumerator<IPort> > pe = node.GetPorts().GetEnumerator(); ;
I got errors from not finding correct match with g++ compiler.
error: no matching function for call to ‘AIR::A<AIR::IConstEnumerator<AIR::IPort> >::AP(AIR::A<AIR::IConstEnumerator<AIR::IPort> >)’
note: candidates are: AIR::A<T>::A(AIR::A<T>&) [with T = AIR::IConstEnumerator<AIR::IPort>]
note: AIR::A<T>::A(T*) [with T = AIR::IConstEnumerator<AIR::IPort>]
What's wrong with the class A? It works well with MSVC.
EDIT
Using copy constructor explicitly seems to solve this issue. A< IConstEnumerator<IPort> > pe(node.GetPorts().GetEnumerator())
Your A copy constructor takes its argument by non-const reference:
A(A<T>& o) : Super(o) { }
^ not const
Your example probably tries to copy a temporary object and the non-const reference can't bind to the temporary object. Visual C++ has an evil extension that allows this to work; you have to be careful to avoid relying on that extension if you want your code to be portable.
If you are trying to mimic auto_ptr's copy constructor, you need to also implement something similar to auto_ptr_ref, which is used as a helper to allow copying temporary auto_ptrs. I describe how this is accomplished in the accepted answer to How could one implement std::auto_ptr's copy constructor?
For what it's worth, deriving from std::auto_ptr is a bit odd; consider using composition instead of inheritance, if you can (there isn't a whole lot in std::auto_ptr that you'd benefit from by deriving from it anyway).
Without the exact code that actually causes the error it's hard to know for sure, but I suspect that from AIR::A<T>::A(AIR::A<T>&) what happening is you're trying to pass a temporary (possibly implicit) object into AIR::A<T>::A and MSVC lets you bind the temporary to the non-const reference parameter, while g++ quite properly prohibits this. Can you make the parameter const?
I'd guess you're lacking A(A<T> const & o).
VS (incorrectly) permits getting a non-const reference to a temporary, g++ behaves properly
It looks compiler talks that it cannot choose right function: constructor A from pointer A<T>::A(T*) or copy constructor A<T>::A(A<T> &). Maybe it worth to change definition to explicit A(T* t) : Super(t) { }