Search code examples
c++17template-specializationpartial-specialization

C++ Templated class function that can detect std::vector


If I have a templated class, I can do the following to detect if a vector was passed:

template<typename T> struct is_vector { static const bool value=false; };
template<typename T> struct is_vector<std::vector<T>> { static const bool value=true; };

template<class T>
class Parser {
public:
    Parser() {}
    void parse(T obj) {
        if (is_vector<T>::value) {
            std::cout << "vector\n";

            //obj.push_back(T {});
        }
        else {
            std::cout << "not vector\n";
        }
    }
};

int main() {
    Parser<int> p1;
    p1.parse(123);

    Parser<std::vector<int>> p2;
    p2.parse({ 1, 2, 3});

    return 0;
}

Output:

not vector
vector

I can detect a vector, yet the compiler complains when I uncomment the push_back call:

main.cpp: In instantiation of ‘void Parser<T>::parse(T) [with T = int]’:
main.cpp:26:14:   required from here
main.cpp:15:17: error: request for member ‘push_back’ in ‘obj’, which is of non-class type ‘int’
             obj.push_back(T {});
             ~~~~^~~~~~~~~

Obviously, an int does not have a push_back function, but the vector does. The is_vector call is evaluated at runtime, but the push_back is caught at compile time.

With partial template specialization, I can do what I want:

template<typename T>
void parse(T obj) {
    std::cout << "not vector: " << obj << "\n";
}

template<typename T>
void parse(std::vector<T> obj) {
    std::cout << "is vector\n";
    for (auto i : obj) std::cout << i << " ";

    obj.push_back(T {});

    std::cout << "\n";
    for (auto i : obj) std::cout << i << " ";
    std::cout << "\n";
}

int main() {
    parse(1);
    parse('a');
    parse(std::vector<int> { 1, 2, 3 });

    return 0;
}

Output:

not vector: 1
not vector: a
is vector
1 2 3 
1 2 3 0 

So, how can I combine these 2 ideas, either at compile-time or at runtime? That is, have a templated class with a function that can handle vectors and non-vectors?


Solution

  • What you're looking for is a new feature in C++17, if constexpr. It's the same as a regular if, except that the condition is evaluated at compile time, and when instantiating the branch(es) will discard the non-taken branch at compile time. The discarded branch does not need to well-formed. So, for your example:

    template<class T>
    class Parser {
    public:
        Parser() {}
        void parse(T obj) {
            if constexpr (is_vector<T>::value) {
                std::cout << "vector\n";
    
                obj.push_back(T {});
            }
            else {
                std::cout << "not vector\n";
            }
        }
    };
    

    See Difference between if constexpr vs if for some more talk on the differences. You can also read the cppreference page on if statements to get a detailed overview of some of the nitty-gritty details.