| Autor |
Nachricht |
SAn
Mitglied
Benutzerprofil
Anmeldungsdatum: 20.12.2007
Beiträge: 166
|
SAn Mitglied
16:52:34 06.08.2012 Titel: |
Different forms of copy constructor |
Zitieren |
Hi everybody.
I trying to learn new OS: Ubuntu Linux. Unfortunately it does not have Visual Studio, so I trying GCC.
I came into following sample which compiles with Visual Studio, but does not compile with GCC:
| C++: | #include <opencv2/core/core.hpp> //OpenCV library version 2.4.1
int main(void) {
cv::Point2f a; //Point consists of 2 floats
cv::Point2d b(a); //Point consists of 2 doubles. ERROR HERE
return 0;
} | |
Error text:
| Code: | error: call of overloaded 'Point_(cv::Point2f&)' is ambiguous
note: candidates are:
/usr/include/opencv2/core/operations.hpp:1617:31: note: cv::Point_<_Tp>::Point_(const CvPoint2D32f&) [with _Tp = double, CvPoint2D32f = CvPoint2D32f]
/usr/include/opencv2/core/operations.hpp:1616:31: note: cv::Point_<_Tp>::Point_(const CvPoint&) [with _Tp = double, CvPoint = CvPoint]
/usr/include/opencv2/core/operations.hpp:1615:31: note: cv::Point_<_Tp>::Point_(const cv::Point_<_Tp>&) [with _Tp = double, cv::Point_<_Tp> = cv::Point_<double>] | |
But this code can be compiled:
| C++: | int main(void) {
cv::Point2f a; //Point consists of 2 floats
cv::Point2d b = a; //Point consists of 2 doubles. No error here.
return 0;
} | |
I used to think that these 2 forms of constructor are identical. Please help me to understand the problem.
P.S. | Code: | g++ --version
g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3 | | |
|
|
|
 |
daddy_felix
Mitglied
Benutzerprofil
Anmeldungsdatum: 11.01.2012
Beiträge: 625
|
daddy_felix Mitglied
16:55:54 06.08.2012 Titel: |
Re: Different forms of copy constructor |
Zitieren |
| SAn schrieb: |
I used to think that these 2 forms of constructor are identical. Please help me to understand the problem. |
In your first snippet, you use the copy constructor. In your second, you use the assignment operator. Thats a big difference. |
|
|
|
 |
pumuckl
Moderator
Benutzerprofil
Anmeldungsdatum: 21.06.2005
Beiträge: 7326
|
pumuckl Moderator
17:10:59 06.08.2012 Titel: |
Re: Different forms of copy constructor |
Zitieren |
| daddy_felix schrieb: | | In your second, you use the assignment operator. | Actually, he does not. Both forms are nearly the same except that the second form (the one with the =) requires the copy-constructor to be available. Not shure why the Compiler complains the first but not the second form, though. The error message suggest there is no straight conversion between cv::Point2f and cv::Point2d, but the latter seems to be a typedef for cv::Point_<double>. Did you try looking in the opencv manual/forums for such cases of ambuguity? |
_________________ Du brauchst Hilfe? - Forenregeln. Den richtigen Code posten - machs uns einfacher dir zu helfen
Don't feed the Help Vampires!
|
|
 |
camper
Mitglied
Benutzerprofil
Anmeldungsdatum: 06.08.2004
Beiträge: 5907
|
camper Mitglied
17:35:32 06.08.2012 Titel: |
|
Zitieren |
This looks like cv::Point2f simply has too many conversion constructors/operators.
In| C++: | int main(void) {
cv::Point2f a; //Point consists of 2 floats
cv::Point2d b(a); //Point consists of 2 doubles. ERROR HERE
return 0;
} | | b ist initialized directly with a. This tells the compiler to enumerate all constructors of cv::Point2d that could possibly be called (by suitably converting the argument a to something that constructor can take). Overload resolution then looks at how these conversion-sequences rank with respect to each other and chooses the constructor with the best ranking conversion sequence. However, user-defined conversion sequences (those that involve a constructor or conversion operator call) usually rank the same because they are ranked based on the conversion needed after calling the conversion constructor/operator. Since the enumerated constructor probably take their arguments by reference, that 2nd conversion is most likely either the identity conversion or a qualification conversion, making them undistinguishable, resulting in an ambiguous call.
in short: consider all possibly conversions that produce something a constructor of cv::Point2d can take, use the best conversion sequence found.
| C++: | int main(void) {
cv::Point2f a; //Point consists of 2 floats
cv::Point2d b = a; //Point consists of 2 doubles. No error here.
return 0;
} | | This is a case of copy initialization. Copy initialization requires the right hand side to be of the same type as the object to initialized. If thats not the case, that argument is converted and the result of the conversion is used to directly initialize the object in question (since the types are the same, this usually means calling the copy or move constructor).
in short: convert to the target type, then initialize directly with the conversion result.
Since this only considers all possible conversion sequences to cv::Point2d, not to any other types cv::Point2d might have a constructor for, this appears to work.
afaik visual c++ (unless severly outdated) and g++ both usually get this part right.
| C++: | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | template<typename _Tp> class CV_EXPORTS Point_
{
public:
typedef _Tp value_type;
// various constructors
Point_();
Point_(_Tp _x, _Tp _y);
Point_(const Point_& pt);
Point_(const CvPoint& pt);
Point_(const CvPoint2D32f& pt);
Point_(const Size_<_Tp>& sz);
Point_(const Vec<_Tp, 2>& v);
Point_& operator = (const Point_& pt);
//! conversion to another data type
template<typename _Tp2> operator Point_<_Tp2>() const;
//! conversion to the old-style C structures
operator CvPoint() const;
operator CvPoint2D32f() const;
operator Vec<_Tp, 2>() const;
//! dot product
_Tp dot(const Point_& pt) const;
//! dot product computed in double-precision arithmetics
double ddot(const Point_& pt) const;
//! checks whether the point is inside the specified rectangle
bool inside(const Rect_<_Tp>& r) const;
_Tp x, y; //< the point coordinates
}; | |
...
looks like a design error:
| C++: | //! conversion to another data type
template<typename _Tp2> operator Point_<_Tp2>() const; | | if that were a conversion constructor instead, there would be no problem.
So g++ is doing the right thing, and either visual c++ in error or using a different source.
There's more actually, since we have both conversion constructors and operators for some types, using copy-initialization will be ambiguous, while direct initialization is not:
| C++: | CvPoint a;
Point_<double> b(a); // ok, calls conversion constructor
Point_<double> c = a; // ambiguous, conversion by constructor or operator | | Looks like this code didn't receive much testing.
As a rule of thumb you never want to have both conversion constructors and operators in the same class (template), esp. not for the same types (sometimes it's unavoidable, but then there's explicit, too). |
Zuletzt bearbeitet von camper am 18:03:57 06.08.2012, insgesamt 5-mal bearbeitet |
|
 |
SAn
Mitglied
Benutzerprofil
Anmeldungsdatum: 20.12.2007
Beiträge: 166
|
SAn Mitglied
08:34:43 07.08.2012 Titel: |
|
Zitieren |
camper, thank you for detailed explanation. I was suspecting something like that.
Here is interesting example:
| C++: | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #include <iostream>
struct MyInt {
int x;
MyInt(int const y): x( y )
{ std::cout << "Constructor 1 called." << std::endl; }
MyInt(MyInt const &y): x( y.x )
{ std::cout << "Constructor 2 called." << std::endl; }
};
int main(void)
{
MyInt i = 1;
return 0;
} | |
GCC output:
| Code: | Constructor 1 called.
Constructor 2 called. | |
Visual Studio output:
| Code: | | Constructor 1 called. | |
As you can see, Visual Studio is better, but GCC is more correct. |
Zuletzt bearbeitet von SAn am 08:37:25 07.08.2012, insgesamt 3-mal bearbeitet |
|
 |
pumuckl
Moderator
Benutzerprofil
Anmeldungsdatum: 21.06.2005
Beiträge: 7326
|
pumuckl Moderator
08:59:13 07.08.2012 Titel: |
|
Zitieren |
|
 |
SAn
Mitglied
Benutzerprofil
Anmeldungsdatum: 20.12.2007
Beiträge: 166
|
SAn Mitglied
13:57:10 27.08.2012 Titel: |
|
Zitieren |
New information from man g++:
| Code: | -fno-elide-constructors
The C++ standard allows an implementation to omit creating a temporary which
is only used to initialize another object of the same type. Specifying this
option disables that optimization, and forces G++ to call the copy
constructor in all cases. | |
So, the following behavior is standard compliant?
| C++: | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #include <iostream>
class MyInt {
private:
int x;
public:
MyInt(int const y): x( y )
{ std::cout << "Constructor 1 called." << std::endl; }
MyInt(MyInt const &y): x( y.x )
{ std::cout << "Constructor 2 called." << std::endl; }
};
int main(void)
{
MyInt i = 1;
return 0;
} | |
Output: | Code: | | Constructor 1 called. | | |
|
|
|
 |
camper
Mitglied
Benutzerprofil
Anmeldungsdatum: 06.08.2004
Beiträge: 5907
|
camper Mitglied
15:10:05 27.08.2012 Titel: |
|
Zitieren |
|
 |
Sone
Mitglied
Benutzerprofil
Anmeldungsdatum: 29.05.2012
Beiträge: 3473
|
Sone Mitglied
19:19:45 27.08.2012 Titel: |
Re: Different forms of copy constructor |
Zitieren |
| SAn schrieb: |
So, the following behavior is standard compliant?
|
Why shouldn't it be? Thats simply another form of calling the corresponding ctor, known as "copy initialisation". pumuckl already explained it pretty well.
And btw: I dare you to say, VC++ is nearly as good as GCC is |
_________________ You want to do X, and you think Y is the best way of doing so. Instead of asking about X, you ask about Y. | Code ist kein Haufen von wahllos zusammengeschmissenen Buchstaben und Zeichen, Code ist Logik pur.
Zuletzt bearbeitet von Sone am 19:20:44 27.08.2012, insgesamt 1-mal bearbeitet |
|
 |
Unregistrierter
|
Unregistrierter
20:28:36 27.08.2012 Titel: |
|
Zitieren |
| C++: | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | #include <iostream>
struct MyInt
{
int x;
MyInt(int const y): x( y )
{
std::cout << "Constructor 1 called." << std::endl;
}
MyInt(MyInt const &y): x( y.x )
{
std::cout << "Constructor 2 called." << std::endl;
}
};
int main()
{
/*
So, what's going on there? In fact it is not that complicated:
1) A instance, named 'i', shall be created.
2) The right operand of the assignment operator is a literal of type 'int', thus your struct 'MyInt' must have a constructor looking like 'MyInt(int)'.
3) Well done, your struct 'MyInt' does have a constructor looking like 'MyInt(int)', thus the compiler is happy.
4)
a) The constructor 'MyInt(int)' is called, creating a temporary. ( like 'MyInt tmp(1);' )
In theory:
b) That created temporary serves as argument for the copy constructor 'MyInt(MyInt const&)'.
c) The copy constructor receives that temporary.
d) The copy constructor is now capable of initializing the actual instance 'i' with the aid of that temporary.
--> Finally two instances were created, the temporary and 'i'.
--> And to be more precise, that two instances are equivalent.
--> Kinda stupid and absolutely unnecessary, isn't it?
In practice:
b) The copy constructor 'MyInt(MyInt const&)' is not called. The constructor 'MyInt(int)' is the only constructor being called.
c) As a consequence, the instance created by 'MyInt(int)' is no temporary but the actual instance 'i'.
--> Finally only one instance was created, namely 'i'.
--> That's pretty cool, isn't it?
--> NOTE: The theoretical process must be feasible (compilable), otherwise the practical process can't be done.
What I'm trying to say is, you will get a compiler error, if the copy constructor is not public.
*/
MyInt i = 1;
} | | |
|
|
|
 |
|
Nächstes Thema anzeigen
Vorheriges Thema anzeigen
Sie können Beiträge in dieses Forum schreiben. Sie können auf Beiträge in diesem Forum antworten. Sie können Ihre Beiträge in diesem Forum nicht bearbeiten. Sie können Ihre Beiträge in diesem Forum nicht löschen. Sie können an Umfragen in diesem Forum nicht mitmachen.
|
|
|
|
|