views:

86

answers:

4

Hi, I'm trying to compile the following code under VC2010.

struct CircValRange
{
    double a,b; // range: [a,b)
};

template <struct CircValRange* Range>
class CircVal
{
    // todo
};


const CircValRange SignedDegRange= {-180., 180.};

CircVal<SignedDegRange> x;

I'm getting

error C2970: 'CircVal' : template parameter 'Range' : 'SignedDegRange' : an expression involving objects with internal linkage cannot be used as a non-type argument
1>          d:\4\circval\circval\circval.h(8) : see declaration of 'CircVal'
1>          d:\4\circval\circval\circval.h(13) : see declaration of 'SignedDegRange'

I am trying to define a templated class CircVal that will receive a struct Range as the templated parameter.

I don't want it to be possible to assign classes with one range to classes with another range (I want them to be different types).

How can I do it?

A: 
template <class Range>
class CircVal
{
    // todo
};

or

template <class Range> class CircVal;
template<>
class CircVal<CircValRange> {...

to instantiate

CircVal<CircValRange> ...
aaa
Tried. Won't work.
Lior Kogan
A: 

I think you are omitting one step. If you want to create a template CircVal that you can specialise on a type, you write:

template<typename Range>
class CircVal
{
   ...
};

and if you then want to specialise it on CircValRange, you create an instance of it as you do further down in your code.

What you seem to try and enforce is that it'll only accept classes derived from a certain base class but unfortunately that's not quite the way templates work - you'll have to find another way to enforce that, maybe by duck typing.

Timo Geusch
I'm trying to create different class types for different ranges, so it won't be possible to assign one to another.
Lior Kogan
It's not by default possible to assign two different instantiations of the same template, e.g. Foo<T> to Foo<S>, unless you go out of your way to write an assignment operator to do that. I think you're overcomplicating this for yourself.
Tyler McHenry
I'm not able to write a compilable code based on your suggestion. Can you add a full example please?
Lior Kogan
+2  A: 
JoshD
Thanks for your suggestions.For your 1st suggestion: Overriding the assignment operator won't give me compile-time errors.For your 2st suggestion: It won't work for doubles.
Lior Kogan
+3  A: 

Someone has recommended a constructor parameter, which I second. But you can still do it as originally desired

struct CircValRange
{
    double a,b; // range: [a,b)
};

template <CircValRange const& Range>
class CircVal
{
    // todo
};


extern const CircValRange SignedDegRange= {-180., 180.};

CircVal<SignedDegRange> x;

But notice that the property that determines the type-identity of CircVal<SignedDegRange> is not the value of SignedDegRange, but the address/identity of it. That is, the following does not work because CircVal<SignedDegRange1> denotes a different type

extern const CircValRange SignedDegRange1 = {-180., 180.};

CircVal<SignedDegRange1> y = x; // error!

As such, an enumeration may be better suited for this

enum RangeKind {
  SignedDegRange,
  UnsignedDegRange
};

const CircValRange Ranges[] = { { -180., -180. }, { 0., 360. } };

template <RangeKind Range>
class CircVal
{
    // todo
};

Or even a traits class with static member functions, similar to a solution someone else had

template <typename Range>
class CircVal
{
    // todo
};

struct SignedDegRange {
  static double min() { return -180.; }
  static double max() { return  180.; }
};

CircVal<SignedDegRange> x;
Johannes Schaub - litb
I had to remove "extern const" from your 1st example. I'm not sure why it won't compile with "const", so I can't protect SignedDegRange from being changed at runtime. Moreover, the compiler won't be able to optimize for real consts. I guess I'll go with your 3rd example, which probably supports better compiler optimization.
Lior Kogan
How do I use the Range in the 3rd example? Adding the following constructor to CircVal won't compile: CircVal() { double d= Range.min(); }
Lior Kogan
Ok. figured it out. Just Add static Range range; to my class, and CircVal() { double d= range.min() }.
Lior Kogan
@Lior it will compile with `extern const` if you change the template parameter to be `const` too. You can use the Range in my third example by just saying `Range::min()` and `Range::max()` of course since they are static member functions.
Johannes Schaub - litb