简体   繁体   中英

Can I do this with a template class in c++

I have a class that is similar to this one:

int add(int a,int b)
{
    return a+b;
}
int sub(int a,int b)
{
    return a-b;
}
class C
{
    int m_functionType;
    C(int functionType)
    {
        m_functionType=functionType;
    }
    int calc(int a, int b)
    {
         switch(m_functionType)
         {
            case 1:  // add
               return add(a,b);
               break;
            case 2: // subtract
                return sub(a,b);
          }
     }
};

and the use it as follow:

main()
{ 
    C add(1);
    C sub(2);
    auto x=add.calc(1,2);
    auto z=sub.calc(2,1);
}

This code works, but the function which should be called is resolved at run time, I am looking for a solution that the solves the functions to call at compile time. something such as this:

template <int func>
class C
{
   int calc(int a, int b)
    {
         switch(func)
         {
            case 1:  // add
               return add(a,b);
               break;
            case 2: // subtract
                return sub(a,b);
          }
     }
};


 main()
{ 
    C<1> add;
    C<2> sub;
    auto x=add.calc(1,2);
    auto z=sub.calc(2,1);
}

Is the above code actually resolves the function during compile time or it still resolve it during run time?

Is there any way that I can do this using a template class? Assuming I want to compile it on Visual studio 2013 and GNU which they have some c++11 capability.

In your example func is resolved at run-time in main :

C add(1);
C sub(2);

Compile-time would mean:

C<1> add;
C<2> sub;

If the change above is acceptable, C++98/03 classic idea is to use function overloads:

template <int v>
struct IntToType {
    enum {val = v};
};

int calc_impl(a, b, IntToType<1>) {
    return add(a, b);
}

int calc_impl(a, b, IntToType<2>) {
    return sub(a, b);
}

template <int func>
class C {
    int calc(int a, int b) {
        return calc_impl(a, b, IntToType<func>());
    }
};

Notes:

  1. calc_impl are just two functions taking three arguments. The name of the third argument is omitted because it is not used. They are overloaded by third parameter, because IntToType<1> and IntToType<2> are two different types.
  2. You can put calc_impl functions into private section of your class for better ergonomics.

From the looks of things, you really want to pass the action to be invoked as a template parameter to the outer function that does the invoking indirectly. Depending on your viewpoint, this could be looked at as a policy class or an instance of the strategy pattern 1 .

Since templates are mostly based on types, you usually want to do this with classes rather than functions. Other than that, it's pretty straightforward, though there are a few different possible approaches.

One approach would be to have calc invoke the specified policy in response to its own operator() being invoked. Code for that would look like this:

#include <iostream>

struct add {
    int operator()(int a, int b) { return a + b; }
};

struct sub {
    int operator()(int a, int b) { return a - b; }
};

template <class C>
struct calc {
    int operator()(int a, int b) {
        return C()(a, b);
    }
};

The code to use this could look something like this:

int main() { 
    auto x=calc<add>()(1,2);
    auto z=calc<sub>()(2,1);
}

Another possibility would be for calc to carry out the calculation when the object is constructed, and return the result on demand. That avoids (among other things) the extra set of parens to create an object, then invoke its operator() to do the calculation. On the other hand, it means adding an implicit conversion operator from the calculation object to return the result of the calculation, which many people dislike. Code going that route might look like this:

template<class C>
struct calc2 {
    int val;
public:
    calc2(int a, int b) : val(C()(a, b)) {}
    operator int() { return val; }
};

// ...
std::cout << calc2<add>(1, 2) << "\n";
std::cout << calc2<sub>(2, 1) << "\n";

It's a little hard to say which is really preferable between these two options (and there are other variations as well). Between these, the choice is basically between ugly invocation and ugly implicit conversion.

Of course, depending on what you're doing in the "strategy" part of things, the decision may be simple--if you really need to carry out the action only on demand, instead of immediately upon creation (and saving only the result until needed) the second isn't really an option at all.

I should add that I'm assuming that the integer you're currently supplying to determine what operation to use was an accidental side-effect of the implementation you'd figured out. In other words, I'm assuming that specifying the operation itself more directly as I've done above is really preferable.


1. A strategy is usually an entire algorithm, not something as trivial as adding vs. subtracting, but I'm assuming that's only an example, and your real application does something a little more substantive.

将在编译时解析哪个函数将被解析,因为任何半体面的现代编译器都可以优化掉switch未使用的情况。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM