C++ - Cast template-type object to specific data t

2019-08-12 06:08发布

I'm using a template function searchByCriteria<T> where I would like to be able to run the function using both a string and a double. I have a list of custom-defined objects that have string and double attributes, and I would like to be able to use this function to check the criteria value (of whatever type, entered by the user), to check for matches in the object attributes of the same type.

I.e. User enters a double value, check the object collection for matching double values. User enters a string value, check the object collection for matching string values.

The problem I am having is once the value is entered, it is passed to another template function to be checked against the elements in the list. And at this point, the T object that is passed as a parameter, needs to be converted to either a double or a string to allow checking for matches.

Here is the code for this part:

//get a sub-list of transactions
//of all that match specified search criteria
template <typename T>
const TransactionList TransactionList::getTransactionsForSearchCriteria(T criteria) const {
    //make a copy of list to avoid deleting existing data
    TransactionList copy(*this);
    //to have appropriate transactions added in
    //then returned as copy
    TransactionList ret;

    //////////////////////////////////////////
    //before checking for matches can start///
    //must ID datatype of T instance//////////
    //////////////////////////////////////////

    //check all transactions until list empty
    while (copy.size() > 0)
    {
        //check that criteria matches transaction attribute
        if (/*converted criteria matches corresponding attribute*/)
        {
            //flag as match
        }
    }
}

As you can see, the parameter value criteria needs to be converted back into a specific data type before the while loop can be entered to check for matches. I am at a slight loss as to how to do this, as I am not aware of any casting methods in C++ that would be useful in this situation.

The only thing I can think of would be something like:

try
{
    //example method
    convertToDouble(criteria);
}
catch (SomeKindOfNumberException ex)
{
    //cannot be a double
    //so must be string
    convertToString(criteria);
}

Any help is greatly appreciated.

Thanks, Mark

3条回答
我欲成王,谁敢阻挡
2楼-- · 2019-08-12 06:28

How about something like this?

#include <iostream>

template<typename T>
T FuncB(T x) {
    std::cout << "generic T ";
    return x;
}

template<>
double FuncB<double>(double x) {
    std::cout << "double ";
    return 0.123;
}

template<>
std::string FuncB<std::string>(std::string x) {
    std::cout << "string ";
    return "xyz";
}

template <typename T>
void FuncA(T param) {
    std::cout << "FuncA: ";
    T tmp = FuncB(param);
    std::cout << tmp << std::endl;
}

int main() {
    std::string s = "abc";

    FuncA(0.1);
    FuncA(s);
    FuncA(1);
}

FuncA can receive any T but it uses FuncB which is specialized for some specific types. If you want you can keep or delete FuncB(T) to support/avoid using unknown types.

The output of the previous code looks like:

FuncA: double 0.123
FuncA: string xyz
FuncA: generic T 1
查看更多
Explosion°爆炸
3楼-- · 2019-08-12 06:31

Maybe you can add a Criterion<T> class holding user value and delegate the test to this class:

template<class T>
class Criterion {
    T m_value;
public:
    Criterion(T&& value) // allow implicit conversion
    : m_value(std::forward<T>(value)) {}
    bool appliesTo(const TransactionList &l); // provide default implementation if any
}

template<>
inline bool Criterion<double>::appliesTo(const TransactionList &l) {
    /* check against double field */
}

template<>
inline bool Criterion<std::string>::appliesTo(const TransactionList &l) {
    /* check against string field */
}

Then your algorithm will look like this:

template <typename T>
const TransactionList TransactionList::getTransactionsForSearchCriteria(Criterion<T> criteria) const {
    TransactionList copy(*this);
    TransactionList ret;
    while (copy.size() > 0)
    {
        if (criteria.appliesTo(copy))
        {
            //flag as match
        }
    }
}
查看更多
闹够了就滚
4楼-- · 2019-08-12 06:43

So your T object is a container, and you haven't given us a lot of information on it, but if you use vectors internally to the object to contain the objects you'll be way ahead.

Secondly if you were to use a pair to contain your vectors you could take advantage of get to select the correct member.

So, for the purposes of writing an answer my TransactionList object will look like this:

typedef pair<vector<string>, vector<double>> TransactionList;

Then we can rewrite your function:

template <typename T>
TransactionList getTransactionsForSearchCriteria(TransactionList result, T criteria) {
    auto& attribute = get<vector<T>>(result);
    auto it = find(attribute.begin(), attribute.end(), criteria);

    //check that criteria matches transaction attribute
    if(it != attribute.end()) {
        //flag as match
    }
    //guessing what you want to do with this: attribute.insert(it, criteria); return result;
}

If you could ensure that the lists were maintained sorted, the standard provides binary search functionality which would dramatically improve your performance for large collections:

template <typename T>
transactionList getTransactionsForSearchCriteria(transactionList result, T criteria) {
    auto& attribute = get<vector<T>>(result);
    auto it = lower_bound(attribute.begin(), attribute.end(), criteria);

    //check that criteria matches transaction attribute
    if(it != attribute.end() && *it == criteria) {
        //flag as match
    }
    //guessing what you want to do with this: attribute.insert(it, criteria); return result;
}

As far as determining if your inputted value is a string or a double when using TransactionList result, take all input as a string then see if you can consume the entire string successfully into a double: Forcing String to int Function to Consume Entire String

size_t size;
double num;
string i;
cin >> i;

if(sscanf(i.c_str(), "%lf%n", &num, &size) == 1 && size == i.size()) {
    result = getTransactionsForSearchCriteria(result, num);
} else {
    result = getTransaztionsForSearchCriteria(result, i);
}

Live Example

查看更多
登录 后发表回答