【发布时间】:2017-02-20 13:37:25
【问题描述】:
我有以下代码:
#include <iostream>
#include <cassert>
#include <utility>
struct real_type {
struct real_category{};
typedef real_category num_category;
};
struct imag_type: public real_type {
struct imag_category{};
typedef imag_category num_category;
};
template<class number_type>
struct number_traits {
typedef typename number_type::num_category num_category;
};
void print_num(double x, double y, typename real_type::num_category) {
assert(y==0);
std::cout<<"real num - x: "<<x<<std::endl;
}
void print_num(double x, double y, typename imag_type::num_category) {
std::cout<<"imag num - x: "<<x<<", y: "<<y<<std::endl;
}
template<int Y=0, typename num_type = typename std::conditional<Y==0, real_type, imag_type>::type >
void print_num(double x) {
typename number_traits<num_type>::num_category num_category;
print_num(x, Y, num_category);
}
int main() {
print_num(1);
print_num<3>(2);
return 0;
}
有输出:
real num - x: 1
imag num - x: 2, y: 3
我对这段代码的行为不太满意。可以看出,我将虚值限制为整数。此外,我必须用结尾的<> 调用 print_num 的虚构变体,这也不优雅。
这种构造的原因是,为了推断出正确的类别标签,我必须检查Y 的值。但是这种检查只能在模板参数列表中进行。
所以我的问题是:是否有可能,同时保持这种标签调度结构(即保持函数void print_num(..., typename real_type::num_category) 和void print_num(..., typename imag_type::num_category) 原样,定义调度函数如void print_num(double x, double y=0)。这意味着(? ) 通过检查 y==0 是否以某种方式推断出正确的 num_category。
我知道我可以通过在void print_num(double x, double y=0) 函数中使用if(y==0)\*then call print_num with real_category tag*\else \*call with imag_category tag*\ 来解决这个问题。这将使 number_traits 类过时(我不想要)。 IE。我想在模板参数列表中做出这个决定。这可能吗?我认为不是(?)
更新:
根据答案我重写了代码:
#include <iostream>
#include <cassert>
#include <utility>
struct real_type {
struct real_category{};
typedef real_category num_category;
struct real_number{ double x; };
typedef real_number num;
num num_x;
};
struct imag_type: public real_type {
struct imag_category{};
typedef imag_category num_category;
struct imag_number{ double x; double y; };
typedef imag_number num;
num num_xy;
};
template<class number_type>
struct number_traits {
typedef typename number_type::num_category num_category;
};
void print_num(real_type r, typename real_type::num_category) {
std::cout<<"real num - x: "<<r.num_x.x<<std::endl;
}
void print_num(imag_type i, typename imag_type::num_category) {
std::cout<<"imag num - x: "<<i.num_xy.x<<", y: "<<i.num_xy.y<<std::endl;
}
template<typename num_type>
void print_num(num_type i) {
typename number_traits<num_type>::num_category num_category;
print_num(i, num_category);
}
int main() {
real_type r_type;
r_type.num_x.x = 1.1;
imag_type i_type;
i_type.num_xy.x = 2.1;
i_type.num_xy.y = 3.2;
print_num(r_type);
print_num(i_type);
return 0;
}
输出:
real num - x: 1.1
imag num - x: 2.1, y: 3.2
它现在正在工作。不完全确定,如果这段代码现在与标签调度概念一致
【问题讨论】:
-
print_num(double x, double y=0) { if (y == 0) print_num(x, y, real_type::num_category{}); else print_num(x, y, imag_type::num_category{});。虽然我看不到重点。使用像print_num(double x, double y=0)这样的签名,您显然希望在运行时检查y。那么,作为一种编译时元编程技术的标签分派有什么意义呢?你说标签调度是不可协商的——但为什么呢?你认为它在这里有什么用途? -
谢谢,这就是我的意思:要获得标签调度在这个特定示例中是否有用的答案:不!因为检查 y==0 是否在运行时完成
-
标签调度用于在编译时根据输入的某些编译时特征选择算法(例如,
std::distance对于随机访问迭代器与前向迭代器的实现方式不同)。我看不出它如何有效地应用于这种情况。 -
进行了编辑。现在工作。由于您似乎是元编程方面的专家,您可以看看这个。
-
我还是不明白这一点。只需有两个(常规的、非模板的)
print_num重载,一个采用real_type参数,另一个采用imag_type,就可以解决问题。也不清楚为什么imag_type是从real_type派生的——正如所写,它带有三个double成员而不是两个。