简体   繁体   中英

Is it possible to have a constructor with 1 input parameter of undeclared type?

Here is what I an trying to do:

  1. Class with two objects ( string myStr and int myInt )

  2. Constructor takes in 1 parameter (data type not fixed).

  3. If parameter is string: myStr = parameter; myInt = parameter.length(); myStr = parameter; myInt = parameter.length();

  4. If parameter is int: myInt = parameter; myStr = "0000...0" myInt = parameter; myStr = "0000...0" (string of length "parameter", set by for loop)

  5. Else : cout << "Error";

Is it possible to do what I am describing in line 2?

I've managed to do some workarounds using only strings and converting data types (able to do this when imposing an artificial lower bound for length of myStr ) however this is causing me other issues further down the line.

Is it possible to have a constructor with 1 input parameter of undeclared type?

Technically, you can have variadic parameters which allows unspecified number of arguments of unspecified types. But I recommend against using it.

The types of all other parameters must be declared.

  • If parameter is string: myStr = parameter; myInt = parameter.length();

  • If parameter is int: myInt = parameter; myStr = "0000...0" (string of length "parameter", set by for loop)

You can easily achieve this using two constructors instead of one. One that accepts a string and another that accepts an integer.

Here is an example of the ideas discussed, overloaded constructors and a templated constructor (mostly just for educational purposes). I think you see why the overloads are considered to be a bit more practical for this case:)

#include <iostream>
#include <string>
#include <type_traits>

//-------------------------------------------------------------------------------------------------
// class wit overloaded constructorss

class my_class_t
{
public:
    explicit my_class_t(const std::size_t& size) :
        m_size{ size }
    {
    }

    explicit my_class_t(const std::string& string) :
        m_string{ string },
        m_size{ string.size() }
    {
    }

    // any other type of parameter to the constructor will just not compile

    std::size_t size() const
    {
        return m_size;
    }

private:
    std::string m_string;
    std::size_t m_size;

};

//-------------------------------------------------------------------------------------------------
// class with templated constructor
// it is a bit more complicated :)

class my_class_template_constructor_t
{
public:
    // this is the syntax for a function that can accept a parameter of any type
    template<typename type_t>
    explicit my_class_template_constructor_t(const type_t& value)
    {
        // select code based on some compile time logic.
        // e.g. if type_t can be converted to the type of m_size then select the first
        // bit to compile (that's what if constexpr is for)
        if constexpr (std::is_convertible_v<type_t,decltype(m_size)>)
        {
            m_size = static_cast<int>(value);
        }
        else
        // if a std::string can be constructed from type_t
        if constexpr (std::is_constructible_v<std::string, type_t>)
        {
            m_string = value;
            m_size = m_string.size();
        }
        else
        // give a compile time error (not a runtime one)
        {
            static_assert(false, "unsupported type for type_t");
        }
    }

    std::size_t size() const
    {
        return m_size;
    }

private:
    std::size_t m_size;
    std::string m_string;
};

//-------------------------------------------------------------------------------------------------

int main()
{
    my_class_t c1{ 42 };
    my_class_t c2{ "Hello world!" };

    std::cout << c1.size() << "\n";
    std::cout << c2.size() << "\n";

    my_class_template_constructor_t ct1{ 42 };
    my_class_template_constructor_t ct2{ "Happy new year!" };

    std::cout << ct1.size() << "\n";
    std::cout << ct2.size() << "\n";

    return 0;
}

The issue is not using a single parameter, but that you need later to find out the type of data of the parameter.

A commonly used trick it's to use either a generic Pointer, or a generic object class reference or Pointer.

MyClass::MyClass ( void* P );
MyClass::MyClass ( void& SomeBaseClass );

Since You are working for more simple data, You may want to try an OO approach like nesting the values in objects, and later, checking the type of the data:

class BaseClass
{
  // ...
} ;

class IntegerClass: BaseClass
{
  int Value;
} ;

class StringClass: BaseClass
{
  String Value;
} ;

And your class can receive a "BaseClass" object reference:

class MyClass
{
  private int MyInt;
  private String MyString;
  
  MyClass ( BaseClass& P ) 
  {
     if ( P is IntegerClass )
     {
       // 
     }
     else if ( P is StringClass )
     {
       //
     }
  }
} ;

int main (...)
{
  IntegerClass *i = new IntegerClass(5);
  StringClass *s = new StringClass ("Hello");
  
  MyClass *O1 = new MyClass(i);
  MyClass *O2 = new MyClass(s);
  
  // Display values of O1 and O2 here
  
  free O2;
  free O1;
   
  free s;
  free i;
  
  return 0;
}

Just my two bitcoins...

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