简体   繁体   中英

Class with both template and non-template constructor

I. Problem description:

Class Derived is a child of class Base . You can not modify class Base . Define constructors and assignement operators for Derived so that it could be constructed both from instances of:

  • Base 1

  • Derived 1

  • N non-polymorphic and not related types Foo1 , ... , FooN 2 .

1 Construction from both Base and Derived is done using Base copy constructor.

2 Construction from all of Foo1 , ... , FooN is done by a generic algorithm.

II. Possible solutions:

1. Brute force:

N+1 separate constructors + N+1 separate assignment operators. Absolutely not elegant. Tons of useless code: N+1 methods declarations in header + N+1 methods implementations in source. Power of templates not used.

2. Template constructor with type restriction

Declare and define regular copy-constructor

Derived::Derived ( const Base& object_reference ) { ... }

Declare template constructor:

template<typename type>
Derived::Derived ( const type& object_reference );

Implement for each of Foo0 , ... , FooN

template<>
Derived::Derived<Foo0> ( const Foo0& object_reference ) { ... }

...

template<>
Derived::Derived<Foo9> ( const Foo9& object_reference ) { ... }

As a result the header will contain only two constructors and only two assignment operators. But we will have to implement N+1 methods in the source. I believe there is a better solution anyway.

III. What will not work:

1. Separating `Base` and `Derived` from others using `dynamic_cast`

template<typename type>
Derived::Derived ( const type& object_reference )
{

    //  This line will not compile since `Foo0`, ... , `FooN` are non-polymorthic
    Base* base_ptr = dynamic_cast <Base*> (&object_reference);

    if ( base_ptr != nullptr )
    {

        //  Construct from `Base`
        return;

    }

    //  Construct from `Foo0`, ... , `FooN`

}

2. Separating `Base` and `Derived` from others using `typeid`

template<typename type>
Derived::Derived ( const type& object_reference )
{

    if
    (
        typeid(typename) == typeid(Foo0)
            ||
            ...
            ||
        typeid(typename) == typeid(FooN)
    }
    {

        //  Construct from `Foo0`, ... , `FooN`
        return;

    }

    else
    {

        //  Construct from `Base`

        //  Here we should call `Base` members which `Foo0`, ... , `FooN` don't have
        //  so the following line will not compile
        //  object_reference.some_method();
        //  And we need to cast "&object_reference" to "Base*" what is not possible
        //  because `Foo0`, ... , `FooN` are not polimorthic

    }

}

IV. The question:

Is there any efficient way, which is not described in section II, to solve the problem?

From the information in your comments, there actually is a commonality between Foo1 and FooN , namely they are all encodings of a socket address. So make a to_string() serialization in the various FooAny classes,

class FooAny // Any runs from 1 to N
{
public:
    std::string to_string() const { /* FooAny specific */ }
private:
    // bla
};

and then use a single template constructor in Derived that delegate to a regular construtor taking a std::string argument

class Derived
{
    explicit Derived(std::string const& s): /* convert data members from string */ {}

    template<class Foo>
    explicit Derived(Foo const& f): Derived(f.to_string()) {} // delegating constructor
};

You don't need to use typeid here:

2. Separating `Base` and `Derived` from others using `typeid`

Just make two non-template ctors and one template ctor for Foo classes:

class Derived : public Base {
 public:
  Derived(const Derived&);
  Derived(const Base&);

  template<class Foo>
  Derived(const Foo&);
};

Here is my two cents. ( Code on Ideone.com)

#include <iostream>
#include <type_traits>

namespace so
{
struct _base_ {};

struct _foo1_{};
struct _foo2_{};
struct _foo3_{};

class _derived_: public _base_
{
 public:
  _derived_() = default;

  _derived_(_derived_ const & _obj)
      : _base_(_obj)
  {
   std::cout << "Constructed from _derived_" << std::endl;
  }

  _derived_(_base_ const & _obj)
      : _base_(_obj)
  {
   std::cout << "Constructed from _base_" << std::endl;
  }

  template <typename _t_, typename = typename std::enable_if<
     std::is_same<_t_, _foo1_>::value || std::is_same<_t_, _foo2_>::value || 
     std::is_same<_t_, _foo3_>::value>::type>
  _derived_(_t_ const &)
      : _base_()
  {
   std::cout << "Constructed from _fooN_ using generic algorithm" << std::endl;
  }

  ~_derived_() noexcept (true) = default;
};
} //namespace so


int main()
{
 so::_base_ b_{};
 so::_derived_ d_{};
 so::_foo1_ f1_{};
 so::_foo2_ f2_{};
 so::_foo3_ f3_{};

 so::_derived_ db_{b_};
 so::_derived_ dd_{d_};
 so::_derived_ df1_{f1_};
 so::_derived_ df2_{f2_};
 so::_derived_ df3_{f3_};

 return (0);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM