开发者

Problem declaring class objects outside of conditional statement because of template function

For my c++ class we were given the task of writing a template class who's class object's type is defined by the user using templates.

Code snipit from main:

if (dataType == "1" || dataType == "int") {  
    simpleVector<int> userArray;  
} else if (dataType == "2" || dataType == "double") {  
    simpleVector<double> userArray;  
} else if (dataType == "3" || dataType == "char") {  
    simpleVector<char> userArray;  
} else if {  
    simpleVector<string> userArray;  
}  
userArray.setDefaultArray();

From this I get error code C2065 - undeclared identifier error. I see why I am getting the error but i do not know how I can declare userArray before I know the data type.

Source Code:

#include <stdio.h>
#include <string>
using std::string;
#include <iostream>
using std::cout;
using std::cin;
using std::endl;

template<class T>
class simpleVector {
public:
    void setDefaultArray ();
    void setArraySize (int size);
    void copy (T *arr);
    void desctruct ();
    int getArraySize ();
    T getElementAt (int index);
    void fillArray();
private:
    int arraySize;
    T *myArray;
};

int main () {
    string dataType;
    int arraySize;
    bool loopCondition = false;

    do {
        cout << "Data Type:"; 
        cin >> dataType;
        if (dataType == "1" || dataType == "2" || dataType == "3" || dataType == "4" 
        || dataType == "int" || dataType == "double" || dataType == "char" || dataType == "string") {
            loopCondition = false;
        } else {
            cout << "WARNING: invalid data type entered." << endl;
            cout << "Valid entries are (1.int, 2.double, 3.char, 4.string)" << endl;
            loopCondition = true;
        }
    } while (loopCondition);

    if (true) 
        int num = 9;
    else
        int num = 7;

    int num2 = num;

    //simpleVector userArray; //?? Review

    if (dataType == "1" || dataType == "int") {
        simpleVector<int> userArray;
    } else if (dataType == "2" || dataType == "double") {
        simpleVector<double> userArray;
    } else if (dataType == "3" || dataType == "char") {
        simpleVector<char> userArray;
    } else if (dataType == "4" || dataType == "char") {
        simpleVector<string> userArray;
    }
    userArray.setDefaultArray();
    cout << "Number of Inputs:"; 
    cin >> arraySize;
    userArray.setArraySize(arraySize);
    userArray.fillArray();

    return 0;
}

//Should call desctruct before this if reusing.
template<class T>
void simpleVector<T>::setDefaultArray() {
    arraySize = 0;
    myArray = NULL; //note: NULL is case sensitive (#include <stdio.h>)
}

template<class T>
void simpleVector<T>::setArraySize (int size) {
        myArray = new T[size];
}

template<class T>
void simpleVector<T>::copy (T *arr) {
//ToDo
}

template<class T>
void simpleVector<T>::desctruct () {
//ToDo
}

template<class T>
int simpleVector<T>::getArraySize () {
//ToDo
}

template<class T>
T simpleVector<T>::getElementAt (int index) {
//ToDo
}

template<class T>
void simpleVector<T>::fillArray() {
    cout << "Enter Array Values" << endl;
    for (int i; i < arraySize; i++) {
        cout <&开发者_运维知识库lt; "Element " + i + ":";
        cin >> myArray[i];
    }
}

Thanks,

Mike


The code in Eugene's answer looks great, but is maybe too complicated for learning C++?

A very simple solution could look like this

  • declare a class vectorBase, which declares all the methods you need in all your vectors
  • let the templated class inherit from vectorBase

template class simpleVector : public vectorBase { ...

  • then declare a pointer of type vectorBase before your

if (dataType == "1" || dataType == "int") ...

  • in the if-block assign the newly created userArrays to the base class pointer
  • later, access the methods through the baseClass pointer, which is identical for all specific template classes


You can't do this, because types determination are a compile-time process. Use inheritance instead templates, if you want to determine types at runtime. Also I can suggest you the "variant" pattern. For example:

#include <memory>
#include <string>

class variant
{
public:

    template <class T>
    variant& operator = (T const& t)
    {
        typedef type<T> assign_type;
        object = std::auto_ptr<assign_type>(new assign_type(t));
        return *this;
    }

    template <class T>
    operator T ()
    {
        typedef type<T> assign_type;
        assign_type& type = dynamic_cast<assign_type&>(*object);
        return type.get();
    }

private:

    class base
    {
    public:

        virtual ~base() {}
    };

    typedef std::auto_ptr<base> base_ptr;

    template <class T>
    class type : public base
    {
    public:

        type(T const& t)
        : object(t)
        {
        }

        T get() const
        {
            return object;
        }

    private:

        T object;
    };

    base_ptr object;
};

struct dummy
{
    int a;
    int b;
    int c;
};

int main()
{
    variant v1, v2, v3, v4;

    v1 = 2;
    v2 = 5.0f;
    v3 = std::string("Pot of gold");
    v4 = dummy();

    int         i = v1;
    float       f = v2;
    std::string s = v3;
    dummy       d = v4;

    return 0;
}


As I understand, the intention of this problem is to teach how template usage is limited by type definition on compile time. It's pretty straightforward that user's choice will be limited by some list of types which developer cared to explicitly specify. Now the question is - how it affects the resulting program?

First, you should realize that code-paths for all possible values of your template argument will be instantiated on compile time. In other words, binary code for setDefaultArray, setArraySize, fillArray and other member functions which you explicitly or implicitly call in your generic algorithm will be generated for int, double, char and std::string template arguments. There's not much you can do to optimize it out of the executable.

However, what you can do is to decide how to store your object(s) in memory in most efficient way. And obviously for your task you need only a single instance of some simpleVector at a time. So you may think of a memory block big enough to keep any simpleVector instantiation and also designating which of them it currently contains. In C++ it will sound like this:

struct SimpleVectors {
    VectorTypeEnum vte;
    union {
        simpleVector<int> v_int;
        simpleVector<double> v_double;
        simpleVector<char> v_char;
        simpleVector<string> v_string;
    };
};

Please note that you can do it only with POD-structures (google for the definition). Inheritance-based approaches eventually boil-down to this kind of layout.

To complete the picture, we just need to connect a processing logic to this data structure:

template <
    typename T
>
inline void handleTask (
    simpleVector <
        T
    >
        & v
)
{
    int arraySize;
    v.setDefaultArray();
    cout << "Number of Inputs:"; 
    cin >> arraySize;
    v.setArraySize(arraySize);
    v.fillArray();
}

The benefit of this approach over the inheritance-based is that you can make your class member functions inline, and the compiler will take care that their calls will be an order of magnitude faster than the virtual member functions.

And finally, the key piece of your main function will look as:

SimpleVectors userArray; 
// we don't really need to initialize userArray.vte in this sample

if (dataType == "1" || dataType == "int") {
    handleTask(userArray.v_int);
} else if (dataType == "2" || dataType == "double") {
    handleTask(userArray.v_double);
} else if (dataType == "3" || dataType == "char") {
    handleTask(userArray.v_char);
} else if (dataType == "4" || dataType == "string") {
    handleTask(userArray.v_string);
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜