Ambiguous constructor call

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • xtrigger303@gmail.com

    Ambiguous constructor call

    Hi to all,
    I'm working on a smart pointer implementation and I'm trying to get
    automatic type conversion between different pointer types. I stumbled
    upon something weird (at least for me) I summarized it in the code
    below. I was expecting both things at the end to work or not work at
    all....
    Any insight?
    Thanks in advance,
    Francesco


    #include <iostream>

    class A;

    //

    class B
    {
    public:

    B() { std::cout << "B()\n"; }

    B( B const & ) { std::cout << "B( B const & )\n"; }

    ~B() { std::cout << "~B()\n"; }

    B & operator=( B const & ) { std::cout << "B & operator=( B const & )
    \n"; return *this; }

    template< typename T >
    operator T() const;
    };

    //

    class A
    {
    public:

    A() { std::cout << "A()\n"; }

    explicit A( int ) { std::cout << "A( int )\n"; }

    A( A const & ) { std::cout << "A( A const & )\n"; }

    ~A() { std::cout << "~A()\n"; }

    A & operator=( A const & ) { std::cout << "A & operator=( A const & )
    \n"; return *this; }
    };

    //

    template< typename T >
    B::operator T() const { std::cout << "B::operato r T() const\n";
    return T(); }

    //

    int main( )
    {
    B obj001;
    A obj002 = obj001; // this works
    //A obj003( obj001 ); // this is ambiguous
    }

  • =?ISO-8859-1?Q?Erik_Wikstr=F6m?=

    #2
    Re: Ambiguous constructor call

    On 2007-09-02 19:58, xtrigger303@gma il.com wrote:
    Hi to all,
    I'm working on a smart pointer implementation and I'm trying to get
    automatic type conversion between different pointer types. I stumbled
    upon something weird (at least for me) I summarized it in the code
    below. I was expecting both things at the end to work or not work at
    all....
    Any insight?
    Thanks in advance,
    Francesco
    >
    >
    #include <iostream>
    >
    class A;
    >
    //
    >
    class B
    {
    public:
    >
    B() { std::cout << "B()\n"; }
    >
    B( B const & ) { std::cout << "B( B const & )\n"; }
    >
    ~B() { std::cout << "~B()\n"; }
    >
    B & operator=( B const & ) { std::cout << "B & operator=( B const & )
    \n"; return *this; }
    >
    template< typename T >
    operator T() const;
    };
    >
    //
    >
    class A
    {
    public:
    >
    A() { std::cout << "A()\n"; }
    >
    explicit A( int ) { std::cout << "A( int )\n"; }
    >
    A( A const & ) { std::cout << "A( A const & )\n"; }
    >
    ~A() { std::cout << "~A()\n"; }
    >
    A & operator=( A const & ) { std::cout << "A & operator=( A const & )
    \n"; return *this; }
    };
    >
    //
    >
    template< typename T >
    B::operator T() const { std::cout << "B::operato r T() const\n";
    return T(); }
    >
    //
    >
    int main( )
    {
    B obj001;
    A obj002 = obj001; // this works
    //A obj003( obj001 ); // this is ambiguous
    }
    >
    Since B can be converted to any type you like, it can be converted to
    either A or int. Both of which are types for which A has a constructor,
    and the compiler have no idea which of them you would like to use. Using
    explicit on a constructor only prevents it from being used like this:

    A a = 1;

    You have to do

    A a(1);

    --
    Erik Wikström

    Comment

    • xtrigger303@gmail.com

      #3
      Re: Ambiguous constructor call

      On Sep 2, 8:52 pm, Erik Wikström <Erik-wikst...@telia. comwrote:
      On 2007-09-02 19:58, xtrigger...@gma il.com wrote:
      >
      >
      >
      Hi to all,
      I'm working on a smart pointer implementation and I'm trying to get
      automatic type conversion between different pointer types. I stumbled
      upon something weird (at least for me) I summarized it in the code
      below. I was expecting both things at the end to work or not work at
      all....
      Any insight?
      Thanks in advance,
      Francesco
      >
      #include <iostream>
      >
      class A;
      >
      //
      >
      class B
      {
      public:
      >
      B() { std::cout << "B()\n"; }
      >
      B( B const & ) { std::cout << "B( B const & )\n"; }
      >
      ~B() { std::cout << "~B()\n"; }
      >
      B & operator=( B const & ) { std::cout << "B & operator=( B const & )
      \n"; return *this; }
      >
      template< typename T >
      operator T() const;
      };
      >
      //
      >
      class A
      {
      public:
      >
      A() { std::cout << "A()\n"; }
      >
      explicit A( int ) { std::cout << "A( int )\n"; }
      >
      A( A const & ) { std::cout << "A( A const & )\n"; }
      >
      ~A() { std::cout << "~A()\n"; }
      >
      A & operator=( A const & ) { std::cout << "A & operator=( A const & )
      \n"; return *this; }
      };
      >
      //
      >
      template< typename T >
      B::operator T() const { std::cout << "B::operato r T() const\n";
      return T(); }
      >
      //
      >
      int main( )
      {
      B obj001;
      A obj002 = obj001; // this works
      //A obj003( obj001 ); // this is ambiguous
      }
      >
      Since B can be converted to any type you like, it can be converted to
      either A or int. Both of which are types for which A has a constructor,
      and the compiler have no idea which of them you would like to use. Using
      explicit on a constructor only prevents it from being used like this:
      >
      A a = 1;
      >
      You have to do
      >
      A a(1);
      >
      --
      Erik Wikström
      In fact I was wondering why the first initialization works ( it is not
      ambiguous it seems...)
      A obj002 = obj001; // this works
      while the second doesn't.
      //A obj003( obj001 ); // this is ambiguous
      I figured that the explicit doesn't help here.
      :-)

      Comment

      • Markus Schoder

        #4
        Re: Ambiguous constructor call

        On Sun, 02 Sep 2007 17:58:17 +0000, xtrigger303 wrote:
        Hi to all,
        I'm working on a smart pointer implementation and I'm trying to get
        automatic type conversion between different pointer types. I stumbled
        upon something weird (at least for me) I summarized it in the code
        below. I was expecting both things at the end to work or not work at
        all....
        Any insight?
        Thanks in advance,
        Francesco
        >
        >
        #include <iostream>
        >
        class A;
        >
        //
        >
        class B
        {
        public:
        >
        B() { std::cout << "B()\n"; }
        >
        B( B const & ) { std::cout << "B( B const & )\n"; }
        >
        ~B() { std::cout << "~B()\n"; }
        >
        B & operator=( B const & ) { std::cout << "B & operator=( B const
        & )
        \n"; return *this; }
        >
        template< typename T >
        operator T() const;
        };
        >
        //
        >
        class A
        {
        public:
        >
        A() { std::cout << "A()\n"; }
        >
        explicit A( int ) { std::cout << "A( int )\n"; }
        >
        A( A const & ) { std::cout << "A( A const & )\n"; }
        >
        ~A() { std::cout << "~A()\n"; }
        >
        A & operator=( A const & ) { std::cout << "A & operator=( A const
        & )
        \n"; return *this; }
        };
        >
        //
        >
        template< typename T >
        B::operator T() const { std::cout << "B::operato r T() const\n"; return
        T(); }
        >
        //
        >
        int main( )
        {
        B obj001;
        A obj002 = obj001; // this works
        The standard mandates that this behaves as if a temporary A object is
        created first from obj001 and then obj002 is copy constructed from this
        temporary object (to further complicate things the actual copy
        construction may be elided but the compiler must check that it would have
        been possible). Creating the temporary object is an implicit conversion
        and hence the explicit A(int) is not considered.
        //A obj003( obj001 ); // this is ambiguous
        This is an explicit constructor call hence A(int) as well as the copy
        constructor are considered.
        }
        --
        Markus Schoder

        Comment

        • xtrigger303@gmail.com

          #5
          Re: Ambiguous constructor call

          On Sep 3, 2:44 am, Markus Schoder <a3vr6dsg-use...@yahoo.de wrote:
          On Sun, 02 Sep 2007 17:58:17 +0000, xtrigger303 wrote:
          Hi to all,
          I'm working on a smart pointer implementation and I'm trying to get
          automatic type conversion between different pointer types. I stumbled
          upon something weird (at least for me) I summarized it in the code
          below. I was expecting both things at the end to work or not work at
          all....
          Any insight?
          Thanks in advance,
          Francesco
          >
          #include <iostream>
          >
          class A;
          >
          //
          >
          class B
          {
          public:
          >
          B() { std::cout << "B()\n"; }
          >
          B( B const & ) { std::cout << "B( B const & )\n"; }
          >
          ~B() { std::cout << "~B()\n"; }
          >
          B & operator=( B const & ) { std::cout << "B & operator=( B const
          & )
          \n"; return *this; }
          >
          template< typename T >
          operator T() const;
          };
          >
          //
          >
          class A
          {
          public:
          >
          A() { std::cout << "A()\n"; }
          >
          explicit A( int ) { std::cout << "A( int )\n"; }
          >
          A( A const & ) { std::cout << "A( A const & )\n"; }
          >
          ~A() { std::cout << "~A()\n"; }
          >
          A & operator=( A const & ) { std::cout << "A & operator=( A const
          & )
          \n"; return *this; }
          };
          >
          //
          >
          template< typename T >
          B::operator T() const { std::cout << "B::operato r T() const\n"; return
          T(); }
          >
          //
          >
          int main( )
          {
          B obj001;
          A obj002 = obj001; // this works
          >
          The standard mandates that this behaves as if a temporary A object is
          created first from obj001 and then obj002 is copy constructed from this
          temporary object (to further complicate things the actual copy
          construction may be elided but the compiler must check that it would have
          been possible). Creating the temporary object is an implicit conversion
          and hence the explicit A(int) is not considered.
          >
          //A obj003( obj001 ); // this is ambiguous
          >
          This is an explicit constructor call hence A(int) as well as the copy
          constructor are considered.
          >
          }
          >
          --
          Markus Schoder- Hide quoted text -
          >
          - Show quoted text -
          Thanks, I think I got it.
          But what if I remove the explicit on the constructor.
          Does the compiler choose the "shortest" implicit conversion?
          This means it chooses:

          1. B::operator T() const [ with T=A ]

          instead of

          1. B::operator T() const [ with T = int ]
          and then
          2. A( int ) // if I remove the explicit this could be used for
          implicit conversion

          It makes sense to choose the "shortest" path... I'm asking just to
          make sure I got it.
          Thanks again and sorry for the newbie questions
          FB

          Comment

          • Frank Birbacher

            #6
            Re: Ambiguous constructor call

            xtrigger303@gma il.com schrieb:
            This means it chooses:
            >
            1. B::operator T() const [ with T=A ]
            >
            instead of
            >
            1. B::operator T() const [ with T = int ]
            and then
            2. A( int ) // if I remove the explicit this could be used for
            implicit conversion
            How is the first onw shorter than the second? It is still ambiguous:

            first case:
            B::operator A ()
            A(A const&)

            second case:
            B::operator int()
            A(int)

            Just because in the first case the intermediate value is already of type
            "A" does not make the conversion shorter. A reasonable approach would be
            to provide a A(B const&) constructor.


            Frank

            Comment

            • =?ISO-8859-1?Q?Erik_Wikstr=F6m?=

              #7
              Re: Ambiguous constructor call

              On 2007-09-03 11:42, xtrigger303@gma il.com wrote:
              On Sep 3, 2:44 am, Markus Schoder <a3vr6dsg-use...@yahoo.de wrote:
              >On Sun, 02 Sep 2007 17:58:17 +0000, xtrigger303 wrote:
              Hi to all,
              I'm working on a smart pointer implementation and I'm trying to get
              automatic type conversion between different pointer types. I stumbled
              upon something weird (at least for me) I summarized it in the code
              below. I was expecting both things at the end to work or not work at
              all....
              Any insight?
              Thanks in advance,
              Francesco
              >>
              #include <iostream>
              >>
              class A;
              >>
              //
              >>
              class B
              {
              public:
              >>
              B() { std::cout << "B()\n"; }
              >>
              B( B const & ) { std::cout << "B( B const & )\n"; }
              >>
              ~B() { std::cout << "~B()\n"; }
              >>
              B & operator=( B const & ) { std::cout << "B & operator=( B const
              >& )
              \n"; return *this; }
              >>
              template< typename T >
              operator T() const;
              };
              >>
              //
              >>
              class A
              {
              public:
              >>
              A() { std::cout << "A()\n"; }
              >>
              explicit A( int ) { std::cout << "A( int )\n"; }
              >>
              A( A const & ) { std::cout << "A( A const & )\n"; }
              >>
              ~A() { std::cout << "~A()\n"; }
              >>
              A & operator=( A const & ) { std::cout << "A & operator=( A const
              >& )
              \n"; return *this; }
              };
              >>
              //
              >>
              template< typename T >
              B::operator T() const { std::cout << "B::operato r T() const\n"; return
              T(); }
              >>
              //
              >>
              int main( )
              {
              B obj001;
              A obj002 = obj001; // this works
              >>
              >The standard mandates that this behaves as if a temporary A object is
              >created first from obj001 and then obj002 is copy constructed from this
              >temporary object (to further complicate things the actual copy
              >construction may be elided but the compiler must check that it would have
              >been possible). Creating the temporary object is an implicit conversion
              >and hence the explicit A(int) is not considered.
              >>
              //A obj003( obj001 ); // this is ambiguous
              >>
              >This is an explicit constructor call hence A(int) as well as the copy
              >constructor are considered.
              >>
              }
              >>
              >--
              >Markus Schoder- Hide quoted text -
              >>
              >- Show quoted text -
              >
              Thanks, I think I got it.
              But what if I remove the explicit on the constructor.
              Does the compiler choose the "shortest" implicit conversion?
              No, when writing 'A obj002 = obj001;' it means that the copy constructor
              will be used (even though it might be elided), so the other constructors
              do not matter.

              --
              Erik Wikström

              Comment

              • xtrigger303@gmail.com

                #8
                Re: Ambiguous constructor call

                On Sep 3, 12:25 pm, Frank Birbacher <bloodymir.c... @gmx.netwrote:
                xtrigger...@gma il.com schrieb:
                >
                This means it chooses:
                >
                1. B::operator T() const [ with T=A ]
                >
                instead of
                >
                1. B::operator T() const [ with T = int ]
                and then
                2. A( int ) // if I remove the explicit this could be used for
                implicit conversion
                >
                How is the first onw shorter than the second? It is still ambiguous:
                >
                first case:
                B::operator A ()
                A(A const&)
                >
                second case:
                B::operator int()
                A(int)
                >
                Just because in the first case the intermediate value is already of type
                "A" does not make the conversion shorter. A reasonable approach would be
                to provide a A(B const&) constructor.
                >
                Frank
                Just to clarify: I'm working on a smart pointer library with different
                templated smart pointers types ( like strong, weak, shared ) and a
                central memory tracking system for debugging purposes. The example
                above was just to show the problem... I'm actually working around the
                problem but I was curious... Anyway the previously mentioned
                simplified example is NOT ambiguous.

                In fact


                B obj1;
                A obj2 = obj1;

                compiles perfectly, this means that the compiler chooses one of the
                two paths.
                So either

                1. B::operator T() [ with T = A ]
                2. A( A const & ) // eliding copy constructor

                or

                1. B::operator T() [ with T = int ]
                2. A( int ) // taking away explicit

                Since the compiler chooses, I guess it chooses the first...
                Thanks for the insights
                FB

                Comment

                • terminator

                  #9
                  Re: Ambiguous constructor call

                  On Sep 2, 11:42 pm, xtrigger...@gma il.com wrote:
                  On Sep 2, 8:52 pm, Erik Wikström <Erik-wikst...@telia. comwrote:
                  >
                  >
                  >
                  >
                  >
                  On 2007-09-02 19:58, xtrigger...@gma il.com wrote:
                  >
                  Hi to all,
                  I'm working on a smart pointer implementation and I'm trying to get
                  automatic type conversion between different pointer types. I stumbled
                  upon something weird (at least for me) I summarized it in the code
                  below. I was expecting both things at the end to work or not work at
                  all....
                  Any insight?
                  Thanks in advance,
                  Francesco
                  >
                  #include <iostream>
                  >
                  class A;
                  >
                  //
                  >
                  class B
                  {
                  public:
                  >
                  B() { std::cout << "B()\n"; }
                  >
                  B( B const & ) { std::cout << "B( B const & )\n"; }
                  >
                  ~B() { std::cout << "~B()\n"; }
                  >
                  B & operator=( B const & ) { std::cout << "B & operator=( B const & )
                  \n"; return *this; }
                  >
                  template< typename T >
                  operator T() const;
                  };
                  >
                  //
                  >
                  class A
                  {
                  public:
                  >
                  A() { std::cout << "A()\n"; }
                  >
                  explicit A( int ) { std::cout << "A( int )\n"; }
                  >
                  A( A const & ) { std::cout << "A( A const & )\n"; }
                  >
                  ~A() { std::cout << "~A()\n"; }
                  >
                  A & operator=( A const & ) { std::cout << "A & operator=( A const & )
                  \n"; return *this; }
                  };
                  >
                  //
                  >
                  template< typename T >
                  B::operator T() const { std::cout << "B::operato r T() const\n";
                  return T(); }
                  >
                  //
                  >
                  int main( )
                  {
                  B obj001;
                  A obj002 = obj001; // this works
                  //A obj003( obj001 ); // this is ambiguous
                  }
                  >
                  Since B can be converted to any type you like, it can be converted to
                  either A or int. Both of which are types for which A has a constructor,
                  and the compiler have no idea which of them you would like to use. Using
                  explicit on a constructor only prevents it from being used like this:
                  >
                  A a = 1;
                  >
                  You have to do
                  >
                  A a(1);
                  >
                  --
                  Erik Wikström
                  >
                  In fact I was wondering why the first initialization works ( it is not
                  ambiguous it seems...)
                  >
                  A obj002 = obj001; // this works
                  >
                  while the second doesn't.
                  >
                  //A obj003( obj001 ); // this is ambiguous
                  >
                  I figured that the explicit doesn't help here.
                  :-)- Hide quoted text -
                  >
                  - Show quoted text -
                  explicit causes the first not to be ambiguous.do:
                  template<typena me T>
                  T& B::To();

                  A obj003(obj001.T o<A>());

                  regards,
                  FM.

                  Comment

                  Working...