我正在使用来自第三方库的类,看起来像
...
if (event.getCode() == KeyCode.ENTER) {
...
event.consume(); // Consume Event
}
...
我想在运行时根据条件使用此类的部分特化,例如
template <typename A = DefaultT, typename B = DefaultT, typename C = DefaultT, typename D = DefaultT, typename E = DefaultT, typename F = DefaultT>
class Vertex {};
我想有条件地选择我想要专门化的模板参数。 我不确定是否存在这个问题的术语,我怀疑它是多重调度的一种风格。
一种简单的方法是编写15(2 ^ 4 -1)个类,如
class MyA {};
class MyB {};
class MyC {};
class MyD {};
bool useA, useB, useC, useD; //these booleans change at runtime
// I want to specialize Vertex depending on the above booleans
// The below line shouldn't compile, this is just to give an idea
typedef typename Vertex <useA ? MyA : DefaultT, useB ? MyB : DefaultT,
useC ? MyC : DefaultT, useD ? MyD : DefaultT> MyVertex;
问题变得更加复杂,因为我必须使用网格&#39;使用专门的顶点类的类
typedef typename Vertex <MyA> MyVertexWithA;
typedef typename Vertex <DefaultT, MyB> MyVertexWithB;
typedef typename Vertex <MyA, MyB> MyVertexWithAB; //and so on...until
typedef typename Vertex <MyA, MyB, MyC, MyD> MyVertexWithABCD;
现在,如果我沿着编写15个类的路径走下去,那么我将不得不为每个不同的网格类型再写15行。并且在使用Mesh类的地方变得越来越复杂。
我坚信这必须由我或编译器来完成。我的问题:
答案 0 :(得分:6)
答案是否定的。如果条件在运行时发生变化,那么你不能根据这些条件专门化/实例化模板,它们需要是常量表达式(毕竟,编译器在编译时执行此工作,在程序开始运行之前 ,所以使用运行时表达式是禁止的。如果在编译时确定它们,那么您可以将constexpr
技巧与std::conditional
结合使用。
正如@Karloy Horvath所提到的,你也可以做一些叫做标签调度的事情,类似于下面的例子:
#include <iostream>
struct tag_yes{};
struct tag_no{};
template <typename T> void f_tag();
template <>
void f_tag<tag_yes>() // specializations
{
std::cout << "YES" << std::endl;
}
template <>
void f_tag<tag_no>()
{
std::cout << "NO" << std::endl;
}
void f(bool condition)
{
if(condition)
f_tag<tag_yes>(); // call the YES specialization
else
f_tag<tag_no>(); // call the NO specialization
}
int main()
{
bool condition = true;
f(condition); // dispatch to the "right" template function
}
或者,您甚至可以推断标签(标准C ++算法如何与各种迭代器类型一起使用)
#include <iostream>
#include <type_traits>
struct Foo
{
using tag = std::true_type;
};
struct Bar
{
using tag = std::false_type;
};
template <typename T> void f_tag();
template <>
void f_tag<std::true_type>() // specializations
{
std::cout << "YES" << std::endl;
}
template <>
void f_tag<std::false_type>()
{
std::cout << "NO" << std::endl;
}
template <typename T>
void f(const T&)
{
f_tag<typename T::tag>(); // dispatch the call
}
int main()
{
Foo foo;
Bar bar;
f(foo); // dispatch to f_tag<std::false_type>
f(bar); // dispatch to f_tag<std::true_type>
}
但是,在您的情况下,多态性可能是通过工厂方法和公共虚拟接口的方式。
答案 1 :(得分:2)
“运行时模板专业化”是绝对禁止的。编译器在编译期间处理模板,生成编译为二进制代码的类。
如果编译器没有生成它,你根本就没有适当的二进制代码来调用,因此你不能在运行时实例化这样的类。
您需要使用运行时多态性。无论是内置的虚拟机制还是简单的插件。
例如,您的Vertex
具有属性InterfaceA
,并且在构造函数中,默认情况下您使用DefaultA
来实现/派生自InterfaceA
。但是你可以传递一些CustomA
,它也来自InterfaceA
。没有解决这个问题。根据在运行时完成的选择,您无法获得编译时机制。
编辑:
如果您拥有所有类并且需要在运行时选择适当的版本。那么Mesh
使用的顶点中有一些共同的接口是合乎逻辑的,因此它们应该来自一个公共类。
因此,您需要创建一个工厂来创建一个适当类型的对象,并将其转换为VertexInterface
并将其返回。
VertexInterface makeVertex(bool useA, bool useB){
if(useA && useB) return VertexInterface<MyA, MyB>();
if(useA && !useB) return VertexInterface<MyA, DefaultT>();
if(!useA && useB) return VertexInterface<DefaultT, MyB>();
// default case
return VertexInterface<DefaultT, DefaultT>();
}
您需要工厂处理所有(未)支持的案例。不幸的是,调度需要手动完成,这是模板和运行时之间的桥梁。