khpatel4991
khpatel4991

Reputation: 98

Using typeid with templates

template<class T>
inline T Library<T>::get_isbn()
{
    T temp;
    cout << "Enter the name/no:" << endl;
    cin >> temp;
    string ka;
    if (typeid(temp) == typeid(ka))
    {
        while (islower(temp[0]))
        {
            cout << " Pls enter the using the first letter as capital" << endl;
            cin >> temp;
        }
    }
}
return temp;
}

I'm creating a template class which can take either integer or string as template parameter and when I create an object of the class with T as string, it's going in the loop and everything's works fine. But when I create an object with int as template parameter, it gives me following two errors:

error C1903: unable to recover from previous error(s); stopping compilation

error C2228: left of '.at' must have class/struct/union

I want that if parameter passed is string, then only the code for checking the first alphabet to be capital should run, else when I give the template parameter as int, it shouldn't check for the first alphabet thing.

Upvotes: 2

Views: 7548

Answers (2)

Michał Jaroń
Michał Jaroń

Reputation: 624

The typeid is not a constexpr, so it cannot impact on the template content during compilation stage. All stuff must be compiled.

C++ 17 has introduced if constexpr statement, so it is possible to define a function in a similar way to your example:


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

template<class T>
inline T get_isbn()
{
    T temp;
    std::cout << "Enter the name/no:" << std::endl;
    std::cin >> temp;
    std::string ka;
    // if (typeid(temp) == typeid(ka)) // It is not a constexpr.
    
    // if constexpr compiles only when condition is met.
    
    // Passing types directly to std::is_same
    // if constexpr (std::is_same<T, std::string>::value)
    
    // Deducing types using decltype()
    if constexpr (std::is_same<decltype(temp), decltype(ka)>::value)
    {
        while (std::islower(temp[0]))
        {
            std::cout << " Pls enter the using the first letter as capital" << std::endl;
            std::cin >> temp;
        }
    }
    return temp;
}

int main()
{
    const auto isbn_int = get_isbn<int>();
    std::cout << "The ISBN<int> is: ["<< isbn_int << "]" << std::endl;
    
    const auto isbn_string = get_isbn<std::string>();
    std::cout << "The ISBN<string> is: ["<< isbn_string << "]" << std::endl;
    return 0;
}

// Sample input/output:
// Enter the name/no:
// 123
// The ISBN<int> is: [123]
// Enter the name/no:
// My-ISBN-Number 
// The ISBN<string> is: [My-ISBN-Number]

Before C++17, you must use template specialization, as @dyp mentioned.

Upvotes: 0

dyp
dyp

Reputation: 39121

An if in C++ is always (semantically) a run-time decision. It may be evaluated by the compiler at compile-time, and the unused branch thrown away. But it may doesn't mean it must. You still have to ensure all branches contain valid code.

In this example, the expression temp[0] is ill-formed if temp is an integer. The simplest solution would be to call an overloaded function inside your generic function -- note: by introducing a typeid-branching, your algorithm inherently isn't generic any more, it requires special treatment for some types.

template<class T>
void get_isbn_impl(T&)
{
    // default implementation
}

void get_isbn_impl(string& str)
{
    // special version for `string`
    while (islower(str[0]))
    {
        cout << " Pls enter the using the first letter as capital" << endl;
        cin >> str;
    }
}

template<class T>
inline T Library<T>::get_isbn()
{
    T temp;
    cout << "Enter the name/no:" << endl;
    cin >> temp;

    get_isbn_impl(temp);

    return temp;
}

It is also possible to specialize either Library<string> (the whole class) or just Library<string>::get_isbn.

Upvotes: 3

Related Questions