Jasmine
Jasmine

Reputation: 16175

C++ cannot add to vector inside template

I'm learning about templates.

I have:

template<typename SQLObject>
std::vector<SQLObject> executeSelectQueryReturnSQLVector(std::string _recordType,
                                                             std::string _sql,
                                                             int _rowCount)
{
    typename std::vector<SQLObject> v;

    if (_recordType == AppConstants::sSQLFieldObject)
    {
        for(int r=0; r < _rowCount; r++)
        {
            SQLFieldObject o;

            o.putFieldNumber(sqlite3_column_int(statement, 0));
            [snip]
            v.push_back(o);
        }
    }

    if (_recordType == AppConstants::sSQLNotificationObject)
    {
        for(int r=0; r < _rowCount; r++)
        {
            SQLNotificationObject o;

             o.putNotificationID(sqlite3_column_int(statement, 0));
             [snip]
             v.push_back(o);
        }
    }

    return v;
}

I'm getting a compiler error on v.push_back(o); that states:

no matching member function for call to 'push_back'

I think it makes sense why, its fuzzy to me, since this is a typename determined at compile time?

Does this mean I have to implement my own push_back() function?

Is "C++ Templates The Complete Guide" by Vandevoorde and Josuttis @ 2003 still apply for C++11?

UPDATE 1: Consider this edit:

template<typename SQLObject>
std::vector<SQLObject> executeSelectQueryReturnSQLVector(std::string _recordType,
                                                             std::string _sql,
                                                             int _rowCount)
{
    //typename std::vector<SQLObject> v;

    if (_recordType == AppConstants::sSQLFieldObject)
    {
        std::vector<SQLFieldObject> v;

        for(int r=0; r < _rowCount; r++)
        {
            SQLFieldObject o;

            o.putFieldNumber(sqlite3_column_int(statement, 0));
            [snip]
            v.push_back(o);
        }

        return v;
    }

    if (_recordType == AppConstants::sSQLNotificationObject)
    {
        std::vector<SQLNotificationObject> v;

        for(int r=0; r < _rowCount; r++)
        {
            SQLNotificationObject o;

             o.putNotificationID(sqlite3_column_int(statement, 0));
             [snip]
             v.push_back(o);
        }

        return v;
    }

    //return v;
}

I get compiler errors on both return v; like:

no viable conversion from 'vector<class SQLFieldObject>' to 'vector<class SQLNotificationObject>'

no viable conversion from 'vector<class SQLNotificationObject>' to 'vector<class SQLFieldObject>'

I'm calling this like:

std::vector<SQLFieldObject> _v = executeSelectQueryReturnSQLVector<SQLFieldObject>    (AppConstants::sSQLFieldObject, getSQLToSelectFields(), rowCount);

and

std::vector<SQLNotificationObject> _v = executeSelectQueryReturnSQLVector<SQLNotificationObject>(AppConstants::sSQLNotificationObject, getSQLToSelectNotifications(), rowCount);

Upvotes: 2

Views: 280

Answers (3)

R Sahu
R Sahu

Reputation: 206607

The type of v is std::vector<SQLObject>. The type of o is SQLFieldObject. Unless there is an automatic way to cast an object of type SQLFieldObject to SQLObject,

v.push_back(o);

it is not an allowable operation.

Update

The errors associated with the updated code are:

The return type of executeSelectQueryReturnSQLVector is std::vector<SQLObject>. The return statements return either std::vector<SQLFieldObject> or std::vector<SQLNotificationObject>. Well, the type of the objects being returned don't match with return type in the function signature.

Dealing with the templates a little bit more elegantly:

 // A template class that returns an appropriate string based on the
 // typename used to instantiate.
 template <typename SQLObject> struct RecordTypeChooser;

 // Specialization for returning the record type for SQLFieldObjects.
 template <> struct RecordTypeChooser<SQLFieldObject>
 {
    static std::string getRecordType() { return AppConstants::sSQLFieldObject; }
 };

 // Specialization for returning the record type for SQLNotificationObjects.
 template <> struct RecordTypeChooser<SQLNotificationObject>
 {
    static std::string getRecordType() { return AppConstants::sSQLNotificationObject; }
 };

 // A template class that constructs an object and returns it.
 // The object type is based on the typename used to instantiate.
 template <typename SQLObject> struct ObjectCreator;

 // Specialization for constructing SQLFieldObjects.
 template <> struct ObjectCreator<SQLFieldObject>
 {
    static SQLFieldObject createObject()
    {
       SQLFieldObject o;
       o.putFieldNumber(sqlite3_column_int(statement, 0));
       return o;
    }
 };

 // Specialization for constructing SQLNotificationObjects.
 template <> struct ObjectCreator<SQLNotificationObject>
 {
    static SQLNotificationObject createObject()
    {
       SQLNotificationObject o;
       o.putNotificationID(sqlite3_column_int(statement, 0));
       return o;
    }
 };



 template<typename SQLObject>
 std::vector<SQLObject> executeSelectQueryReturnSQLVector(std::string _recordType,
                                                          std::string _sql,
                                                          int _rowCount)
 {
    typename std::vector<SQLObject> v;

    // Not sure whether you need this any more.
    if (_recordType == RecordTypeChooser<SQLObject>::getRecordType())
    {
       for(int r=0; r < _rowCount; r++)
       {
          v.push_back(ObjectCreator<SQLObject>::createObject());
       }
    }

    return v;
 }

Update: Fully compiled and linked source

 #include <vector>
 #include <string>

 struct SQLFieldObject {};
 struct SQLNotificationObject {};

 // A template class that returns an appropriate string based on the
 // typename used to instantiate.
 template <typename SQLObject> struct RecordTypeChooser;

 // Specialization for returning the record type for SQLFieldObjects.
 template <> struct RecordTypeChooser<SQLFieldObject>
 {
    static std::string getRecordType() { return "SQLFieldObject"; }
 };

 // Specialization for returning the record type for SQLNotificationObjects.
 template <> struct RecordTypeChooser<SQLNotificationObject>
 {
    static std::string getRecordType() { return "SQLNotificationObject"; }
 };

 // A template class that constructs an object and returns it.
 // The object type is based on the typename used to instantiate.
 template <typename SQLObject> struct ObjectCreator;

 // Specialization for constructing SQLFieldObjects.
 template <> struct ObjectCreator<SQLFieldObject>
 {
    static SQLFieldObject createObject()
    {
       SQLFieldObject o;
       // o.putFieldNumber(sqlite3_column_int(statement, 0));
       return o;
    }
 };

 // Specialization for constructing SQLNotificationObjects.
 template <> struct ObjectCreator<SQLNotificationObject>
 {
    static SQLNotificationObject createObject()
    {
       SQLNotificationObject o;
       // o.putNotificationID(sqlite3_column_int(statement, 0));
       return o;
    }
 };



 template<typename SQLObject>
 std::vector<SQLObject> executeSelectQueryReturnSQLVector(std::string _recordType,
                                                          std::string _sql,
                                                          int _rowCount)
 {
    typename std::vector<SQLObject> v;

    // Not sure whether you need this any more.
    if (_recordType == RecordTypeChooser<SQLObject>::getRecordType())
    {
       for(int r=0; r < _rowCount; r++)
       {
          v.push_back(ObjectCreator<SQLObject>::createObject());
       }
    }

    return v;
 }


 void foo()
 {
    std::vector<SQLFieldObject> v1 = executeSelectQueryReturnSQLVector<SQLFieldObject>("SQLFieldObject",
                                                                                       "",
                                                                                       10);

    std::vector<SQLNotificationObject> v2 = executeSelectQueryReturnSQLVector<SQLNotificationObject>("SQLNotificationObject",
                                                                                       "",
                                                                                       10);
 }

 int main() {}

Upvotes: 2

rockoder
rockoder

Reputation: 747

#include <iostream>
#include <vector>

using namespace std;

struct A
{
    int a;
};

struct B
{
    char b;
};

template<typename T>
vector<T> fun(char type)
{
    if (type == 'A')
    {
        vector<T> v;
        // generate objects of A and append to v
        return v;
    }
    else
    {
        vector<T> v;
        // generate objects of B and append to v
        return v;
    }
}

int main()
{
    vector<A> v = fun<A>('A');

    return 0;
}

Upvotes: 0

paulus
paulus

Reputation: 637

The types in the vector and in the instances differ. Vector saves only the pristine classes (and cannot save children due to memory allocation issues otherwise). You may however store pointers to the classes. Consider this:

typedef boost::shared_ptr<SQLObject> SQLObjectPtr;
typedef std::vector<SQLObjectPtr> SQLObjectPtrVector;

...
for(int r=0; r < _rowCount; r++)
{
    SQLFieldObjectPtr o(new SQLFieldObject);

    ...
    v.push_back(o);
}

Upvotes: 0

Related Questions