Search code examples
c++templatesc++11nurbs

C++ template specialized getters


I am currently writing a program working with NURBS surfaces, where you can perform algorithms in two directions (U and V). To avoid code duplication I tried to use templates, but I am by no means experienced using them. Here is a small example of what I would like to do:

#include <iostream>

enum class Dir {
    U, V
};

struct Foo {

    unsigned cu, cv;

    Foo(unsigned cu, unsigned cv) : cu(cu), cv(cv) {};

    template<Dir>
    const Dir otherDir();

    template<>
    const Dir otherDir<Dir::V>() {
        return Dir::U;
    }

    template<>
    const Dir otherDir<Dir::U>() {
        return Dir::V;
    }

    template<Dir>
    unsigned count();

    template<>
    unsigned count<Dir::U>() {
        return cu;
    }

    template<>
    unsigned count<Dir::V>() {
        return cv;
    }

    template<Dir d>
    unsigned totalCount() {
        auto c = count<d>();
        auto cOther = count<otherDir<d>()>();
        return c * cOther;
    }

};

int main() {
    Foo f(3,2);
    std::cout << (f.count<Dir::U>() == 3) << std::endl;
    std::cout << (f.otherDir<Dir::U>() == Dir::V) << std::endl;
    std::cout << f.totalCount<Dir::U>() << std::endl;
}

but this doesn't compile due to the last line in main (VS2015, but I don't think it's compiler's fault):

1>...\main.cpp(42): error C2672: 'Foo::count': no matching overloaded function found
1>...\main.cpp(52): note: see reference to function template instantiation 'unsigned int Foo::totalCount<Dir::U>(void)' being compiled
1>...\main.cpp(42): error C2975: 'unnamed-parameter': invalid template argument for 'Foo::count', expected compile-time constant expression
1>...\main.cpp(27): note: see declaration of 'unnamed-parameter'
1>...\main.cpp(43): error C3536: 'cOther': cannot be used before it is initialized

Only way I got close to the functionality above is by specifying both the main direction as well as the other direction as the template arguments like this:

struct Foo {

    ...

    template<Dir d, Dir otherD>
    unsigned totalCount() {
        auto c = count<d>();
        auto cOther = count<otherD>();
        return c * cOther;
    }

};

int main() {
    Foo f(3,2);
    std::cout << f.totalCount<Dir::U, Dir::V>() << std::endl;
}

but that doesn't seem to be very elegant.


Solution

  • otherDir<d>() is not constexpr, and cannot be used in template argument (count<otherDir<d>()>()).

    You may add constexpr (and static) to that method, or use old struct to handle that:

    template <Dir> struct otherDir;
    
    template<>
    struct otherDir<Dir::U>
    {
        static constexpr Dir value = Dir::V;
    };
    
    template<>
    struct otherDir<Dir::V>
    {
        static constexpr Dir value = Dir::U;
    };
    

    Demo