开发者

Limit function/constructor arguments to members of a particular type

If I have a templated class that takes another class as a parameter and which is constructed from a particular type, is there anyway to limit its range of possible construction values to those that are only members of the parameter class?

So, if I have

template <typename T>
struct Foo {
    Foo(int v)
        : value(v) {}

    int value;
};

and,

struct Bar {
    static const int valid1 = 6;
    static const int valid2 = 9;
    static const int valid3 = 42;
};

can I limit the valid values that can be passed to the constructor of Foo<Bar> to Bar::valid1 ... Bar::valid3?

With this example, I could use an enum in Bar that has a specific name, specified in Foo's constructor which would take members of the enum rather than plain ints, but this has the problem that any value can be cast to the enum, so it is easily broken, and more importantly is not extendible to non-integral types.

A strong typedef would do the trick I think, and I believe there's one in boost but I'm looking for solutions I can implement myself with the standard library if possible.

I thought about function pointers, so instead of static const int's in Bar I would have functions that return the values and the constructor of Foo wold accept a function pointer. However static function开发者_如何学编程s behave just like globals with repect to function pointers so there is no Bar qualification required when calling it in Foo so any global function returning an int could be used. It could be made to work by not using static functions in Bar and maintaining an instance of T [=Bar] in Foo and calling the function pointer against this instance, but this seems a bit messy, functions that naturally should be static are not, and I have an instance of a struct which really should be just a container for constants.

I wonder if there is a standard way of doing this, or what you would recommend.


I have a rather hackish solution if you know the valid numbers you're going to use at compile time. If there's chance that the valid numbers can change, it won't be much use.

Start by using a named-type template parameter rather than the constructor.

template <typename T, int Bar>
struct Foo {
private:
    Foo() { }
};

Notice I've made the constructor private. You won't be able to instantiate this class (or you'll get a compile time error, which is nice).

To instantiate the ones you want, we can specialize the template to those values only, and provide public constructors so you can instantiate them.

template <typename T>
struct Foo<T, 6>{
    Foo() : value(6) { 
    }
    int value;
};

It's now valid to use Foo<int, 6> foo, but Foo<int, 7> foo etc wont compile. Do this for each contsraint. (And yes, I realize it is ugly). You can reduce this syntax a lot by creating another class, let's call it FooInternal - which you will implement all your generic code inside, and having each of your specialized classes derive from it. Give it a protected constructor to prevent instantiation.

template <typename T, int Bar>
struct FooInternal {
protected:
    FooInternal() : value(Bar) {
    }
public:
    int value ;
};

The specializations can then be reduced to one liners.

template<typename T> struct Foo<T, 9> : FooInternal<T, 9> {};
template<typename T> struct Foo<T, 42> : FooInternal<T, 42> {};


Ignore my other answer, I read the question again and think it's suitable for this. Along the same lines though, you can create a struct with a private constructor, and another nested one with a protected constructor. Each of your valid numbers can derive from the internal struct. This makes a kind of strongly-typed enum, which I guess is what you want.

struct Bar {
private:
    Bar() { }

    struct BarInternal {
    protected:
        BarInternal(int v) : value(v) {}
        int value;
    public:
        operator int() { //Allow cast back to int.
            return value;
        }
    };
public:
    struct Valid1 : public BarInternal {
        Valid1() : BarInternal(6) { }
    };

    struct Valid2 : public BarInternal {
        Valid2() : BarInternal(9) { }
    };
};

Then adjust your template to take a Bar::BarInternal in it's constuctor.

template <typename T>
struct Foo {
    Foo(Bar::BarInternal v)
        : value(v) {}

    int value;
};
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜