如何将浮点数作为模板参数传递

How can I pass floating point numbers as template parameters?

本文关键字:参数传递 浮点数      更新时间:2023-10-16

我正在进行一个元编程项目,我们希望能够将浮点数作为模板参数传递。我不确定这个问题是否更适合Stack Exchange,但我认为它有点概念性,所以选择了这个网站。

基本上,我想这样做:

template <double var>
double fun() { return var; }
int main()
{
  double myDoble = fun<1.0>();
  return 0;
}

显然,这是不可能的。这个链接(http://en.cppreference.com/w/cpp/language/template_parameters)清楚地表明,作为模板参数传递的类型必须是以下类型之一:

integral type
enumeration
pointer to object or to function
lvalue reference to object or to function
pointer to member object or to member function
std::nullptr_t (since C++11)

然而,我可以传递指向对象的指针(可能是指向包含双精度体的结构体的指针?)这一事实使我怀疑,通过传递指向这种双精度体的指针,可能有一种方法可以实现上述目的。

所以我试了这个:

template <double* pVar>
double fun()
{
    return *pVar;
}
int main() {
    static const double param = 1.2;
    double value = fun<&param>();
    return 0;
}

给出如下错误:

prog.cpp:9:29: error: ‘& param’ is not a valid template argument of type ‘double*’ because ‘param’ has no linkage
  double value = fun<&param>();

我认为问题可能是我使用了一个原语,所以我尝试了这个:

struct D
{
    double val;
};
template <D* pD>
double fun()
{
    return pD->val;
}
int main() {
    static const D d{1.2};
    double value = fun<&d>();
    return 0;
}

给出相同的错误。

对于解决这个问题,有人有其他的想法吗?如果人们能克制自己不要回答"这是不可能的",我会很感激;我知道这是不可能的,但我还是想做!div;)

如果你真的想传递double's给模板化的函数,你应该使用包装器。像这样:

struct D
{
    operator double(){ return 1.2; }
};
template <typename T>
double fun()
{
    T t;
    return t;
}
int main()
{
    double value = fun<D>();
    return 0;
}

但也许更有用的是定义一个宏,像这样:

#define DBLWRAPPER(dbl) typedef struct { operator double(){ return dbl; } }
int main()
{
    DBLWRAPPER(1.2) DBL1_2;
    DBLWRAPPER(2.45) DBL2_45;
    double value1_2 = fun<DBL1_2>();
    double value2_45 = fun<DBL2_45>();
    return 0;
}

这个适合我:

template <double* pVar>
double fun()
{
    return *pVar;
}
double param = 1.2;
int main() {
   double value = fun<&param>();
   return 0;
}

我使用的是g++ 4.8.2

这里的问题是只有带链接的对象才能用作模板非类型地址参数,我们可以通过c++标准草案14.3.2 模板非类型参数看到这一点,其中说(强调我的):

非类型、非模板模板形参的模板实参应为:

,并包括以下项目符号:

指定对象地址的常量表达式(5.19)静态存储时长,外部或内部链接或a带有外部或内部链接的功能[…]

这正是你的错误所说的:

[…因为' param '没有链接

,我们可以看到块作用域变量除了一些例外没有链接,这在3.5 程序和链接一节中有介绍,其中说:

未被这些规则覆盖的名称没有链接。此外,除了注意,在块作用域(3.3.3)声明的名称没有链接。一个类型当且仅当:

,不包括任何例外情况。

全局声明,可以解决这种情况下的问题:

static double param = 1.2;
int main()
{
    double value = fun<&param>();
    return 0;
}

如果您希望*pVar是一个编译时常数,以优化fun()并在仅接受常数的地方使用它(例如作为模板参数或数组维度),您需要接受它为const double* const并使paramconstexpr(下面的代码也在ideone.com这里)…

template <int n>
struct S { };
template <const double* const pVar>
double fun()
{
    S<(int)*pVar> s;
    return *pVar;
}
constexpr double param = 1.2;
int main()
{
   double value = fun<&param>();
}

请注意,如果代码中的不同位置为模板提供指向具有相同值的不同double的指针,那么您可能会在可执行文件中获得不同的实例化(即无意义的"代码膨胀",特殊化尝试失败…)。

c++ 11的解决方案(将float编码为int):

constexpr int f2i_scaler = 100000;
constexpr int operator"" _i(long double v)
{
    return v * f2i_scaler < std::numeric_limits<int>::max() ||
                   v * f2i_scaler > std::numeric_limits<int>::lowest()
               ? v * f2i_scaler
               : throw std::invalid_argument("Overflow!");
}
template <int n>
struct S {
   void show(){
       std::cout << (double)n/f2i_scaler << "n";
   }
};
S<1.5_i> s;