【问题标题】:Refactoring tag dispatching structure重构标签调度结构
【发布时间】: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

我对这段代码的行为不太满意。可以看出,我将虚值限制为整数。此外,我必须用结尾的&lt;&gt; 调用 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 成员而不是两个。

标签: c++ c++11 templates tags


【解决方案1】:

我认为您最好在运行时而不是编译时检查虚部。这是一个使用std::complex的示例

#include <complex>
#include <iostream>
#include <limits>
#include <sstream>

template<class T>
auto print(std::complex<T> const& c)
{
    std::stringstream sstr;
    if (std::abs(c.imag()) < std::numeric_limits<T>::epsilon()) {
        sstr << c.real();    
    } else {
        sstr << c;    
    }
    return sstr.str();
}

int main()
{
    auto const c1 = std::complex{1.0};
    auto const c2 = std::complex{2.0, 3.0};

    std::cout << print(c1) << "\n"; // prints: 1
    std::cout << print(c2) << "\n"; // prints: (2, 3)
}

Live Example

【讨论】:

  • 是的,我当然知道有 1000 种解决方案可以更好地解决这个特定示例。但这不是我问题的重点。我在 C++ 下的最后两个问题遇到了这个问题。我对为我的示例找到任何解决方案并不感兴趣,但正如 Igor Tandetnik 正确指出的那样,解决方案仅限于特定概念。我发布此类问题是为了获得反馈,我是否正确理解了一个概念,以及某个特定概念是否可以应用于特定示例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-03
  • 1970-01-01
相关资源
最近更新 更多