Hypercell ein ] Hypercell aus ] Zeige Navigation ] Verstecke Navigation ]
c++.de  
   

Die mobilen Seiten von c++.de:
http://m.c-plusplus.de
Infos hier [BETA]

  
c++.de :: C++ (auch C++0x und C++11) ::  Different forms of copy constructor  
Gehen Sie zu Seite 1, 2  Weiter
  Zeige alle Beiträge auf einer Seite
Auf Beitrag antworten
Autor Nachricht
SAn
Mitglied

Benutzerprofil
Anmeldungsdatum: 20.12.2007
Beiträge: 166
Beitrag 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
Beitrag 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
Beitrag 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
Beitrag 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
Beitrag 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
Beitrag pumuckl Moderator 08:59:13 07.08.2012   Titel:              Zitieren

SAn schrieb:
As you can see, Visual Studio is better, but GCC is more correct.
AFAIK, both are correct. For copy-initialization, the Copy-Ctor has to be available, but does not ned to be called i.e. may be omitted completely.

_________________
Du brauchst Hilfe? - Forenregeln. Den richtigen Code posten - machs uns einfacher dir zu helfen
Don't feed the Help Vampires!
SAn
Mitglied

Benutzerprofil
Anmeldungsdatum: 20.12.2007
Beiträge: 166
Beitrag 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
Beitrag camper Mitglied 15:10:05 27.08.2012   Titel:              Zitieren

yes
Sone
Mitglied

Benutzerprofil
Anmeldungsdatum: 29.05.2012
Beiträge: 3473
Beitrag 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 :cool: :D

_________________
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





Beitrag 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;
}
c++.de :: C++ (auch C++0x und C++11) ::  Different forms of copy constructor  
Gehen Sie zu Seite 1, 2  Weiter
Auf Beitrag antworten

Zeige alle Beiträge auf einer Seite




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.

Powered by phpBB © 2001, 2002 phpBB Group :: FI Theme

c++.de ist Teilnehmer des Partnerprogramms von Amazon Europe S.à.r.l. und Partner des Werbeprogramms, das zur Bereitstellung eines Mediums für Websites konzipiert wurde, mittels dessen durch die Platzierung von Werbeanzeigen und Links zu amazon.de Werbekostenerstattung verdient werden kann.

Die Vervielfältigung der auf den Seiten www.c-plusplus.de, www.c-plusplus.info und www.c-plusplus.net enthaltenen Informationen ohne eine schriftliche Genehmigung des Seitenbetreibers ist untersagt (vgl. §4 Urheberrechtsgesetz). Die Nutzung und Änderung der vorgestellten Strukturen und Verfahren in privaten und kommerziellen Softwareanwendungen ist ausdrücklich erlaubt, soweit keine Rechte Dritter verletzt werden. Der Seitenbetreiber übernimmt keine Gewähr für die Funktion einzelner Beiträge oder Programmfragmente, insbesondere übernimmt er keine Haftung für eventuelle aus dem Gebrauch entstehenden Folgeschäden.