0

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.

Alexei - check Codidact
  • 22,016
  • 16
  • 145
  • 164
Nakamp
  • 120
  • 1
  • 8
  • Side note: Be careful about VS non-standard extension, the above does not compile with g++, even without `totalCount` (see http://stackoverflow.com/questions/2097811/c-syntax-for-explicit-specialization-of-a-template-function-in-a-template-clas). – Holt Mar 07 '16 at 14:05
  • And I don't really see the point of having template here, why not simply pass the direction as an argument to the function? – Holt Mar 07 '16 at 14:07
  • `otherDir()` is not `constexpr`, and cannot be used in template argument. – Jarod42 Mar 07 '16 at 14:13
  • Just have direction as an argument. If you really want this at compile, I recommend template whatever container you have to return forward and reverse iterators instead writing a novel to deduplicate 2 lines. – KevinZ Mar 07 '16 at 17:46

2 Answers2

0

Probably, problem is here:

auto cOther = count<otherDir<d>()>();

Templates are "resolved" in compile time. You should use constexpr for otherDir<d>() because just simple const doesn't mean that method can be evaluated in compile time.

More traditional approach is to have structure otherDir instead of method. Create structure template, two instantiations would be with static const value = V or U.

Or you can replace enum with empty structs.

Or, what's even better, you can try to implement it without templates and create class for dimension and have one object per each dimension.

George Sovetov
  • 4,942
  • 5
  • 36
  • 57
0

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

Jarod42
  • 203,559
  • 14
  • 181
  • 302