在编译时将枚举与模板类相关联

时间:2016-11-03 17:05:22

标签: c++ templates types type-conversion

我有一个模板类,如下所示:

template<typename A> struct TaskInfo{
    typedef A       _A;
    static void bar(A a){blah blah...} };

template <typename TaskInfo> class Task   {
    typedef typename TaskInfo::_A A;
    bar(A a){
        blah blah...
        TaskInfo::bar(a);
    }   
}

我有一个包含这些类的集合的对象:

using TaskInfoX= TaskInfo<int>; //ignore the bar implementation for the time being.
using TaskInfoY= TaskInfo<double>;

class TaskCollection(){ 
    TaskCollection(){
        auto Task1=new Task<TaskInfoX>;
        auto Task2=new Task<TaskInfoY>;
        Register(Task1);
        Register(Task2);
    }
Register(...);
}

我想知道是否可以定义枚举列表:

enum TaskEnum
{
    Etask1,
    Etask2
};

和一个函数getTask,以便在我的应用程序中我可以:

int main {
    TaskCollection collection;
    int testInt;
    double testDouble;
    collection.getTask(Etask1)->bar(testInt);
    //collection.getTask(Etask1)->bar(testDouble);  //want compile error.
    collection.getTask(Etask2)->bar(testDouble);
}

我知道我可以拥有CRTP或虚拟继承等价物,允许我为bar()传递可变参数但我希望在编译时对bar函数的参数进行类型检查。这在C ++中是不可能的吗?

更新:为错字道歉。那意味着:getTask(task1)。基本上外部世界并不知道任务的基础结构,只能根据他们的公共枚举密钥来了解它们。另请注意,通常会有其他任务可能重用typeInfoX参数。

4 个答案:

答案 0 :(得分:1)

首先,如果类型不是完全匹配,则有错误,您可以使用以下内容:

template <typename T>
class Task
{
public:
    using type = typename T::type;

    void bar(type a) { T::bar(a); }

    template <typename U>
    std::enable_if_t<!std::is_same<std::decay_t<U>, type>::value>
    bar(U&&) = delete;
};

然后,有一些帮手:

template <TaskEnum> struct TaskMap;

template <> struct TaskMap<Etask1> { using type = Task<TaskInfoX>; };
template <> struct TaskMap<Etask2> { using type = Task<TaskInfoY>; };

您的收藏可能类似于:

class TaskCollection
{ 
public:
    Task<TaskInfoX> taskX;
    Task<TaskInfoY> taskY;

    template <TaskEnum E>
    typename TaskMap<E>::type&
    getTask();
};

template <>
Task<TaskInfoX>& TaskCollection::getTask<Etask1>() { return taskX; }

template <>
Task<TaskInfoY>& TaskCollection::getTask<Etask2>() { return taskY; }

最终用法:

collection.getTask<Etask1>().bar(testInt);
collection.getTask<Etask1>().bar(testDouble);//error:call to deleted member function 'bar'
collection.getTask<Etask2>().bar(testDouble);

Demo

答案 1 :(得分:0)

我认为你需要std::tuple

auto my_tuple = std::make_tuple(Task<TaskInfoX>(), Task<TaskInfoY>());
std::get<Task<TaskInfoX>>(my_tuple).bar(12);
// actually, this is not an error because double can be convert to int
std::get<Task<TaskInfoX>>(my_tuple).bar(12.323);

答案 2 :(得分:0)

考虑到Task1和Task2存储在TaskCollection类中的方式,我无法看到将getTask实现为模板的明显方法,但您可以重载它。

class TaskCollection
{
    //...
    Task<TaskInfoX> GetTask(Task<TaskInfoX> task)
    {
        return Task1;
    }

    Task<TaskInfoY> GetTask(Task<TaskInfoY> task)
    {
        return Task2;
    }
};

如果您的真实案例使用double和int,那么您需要将转换从double压缩到int以实现此编译时错误...

// collection.getTask(任务1) - &GT;巴(testDouble); //想要编译错误。

执行此操作的一种方法是在TaskInfo

中声明但不定义bar的模板版本
template<typename A> struct TaskInfo
{   
    static void bar(A a){blah blah...}
    template<typename T> static void bar(T a); // deliberately not defined
    //...
};

你仍然为你想要处理的类型定义现有的bar函数,但是现在,在你想要失败的情况下,编译器将尝试调用bar的模板版本(使用T作为double)优先使用从double到int的转换来调用int版本。 VS2015中产生的错误是

错误LNK2019:未解析的外部符号“public:static void __thiscall C :: bar(double)

但是如果没有代码试图调用它,那么它没有被定义并且没有错误并不重要。

(我不清楚关于枚举的问题是什么)

答案 3 :(得分:0)

您始终可以使用基元模板化具体类。所以而不是:

using TaskInfoX= TaskInfo<int>;
using TaskInfoY= TaskInfo<double>;

你可以拥有

template<> class Task<TaskEnum::Task1> : public TaskInfo<int>{}
template<> class Task<TaskEnum::Task2> : public TaskInfo<double>{}

然后定义模板函数:

template<TaskEnum taskE>    
Task<taskE>* getTask() {}

您需要从基类派生模板类Task,以便将其放在地图中,并且应该定义地图

std::map<TaskEnum,TaskBase*>    taskMap;

在getTask中你可以做一个静态演员:

static_cast<Task<taskE>* >(taskBasePtr);