我有一个模板类,如下所示:
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参数。
答案 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);
答案 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);