需要一种方法在仅头文件库中具有任何类型的模板化常量

时间:2014-01-23 00:40:39

标签: c++ templates namespaces constants

goals:
1. store constants with their assignments in a header
2. keep constants in a namespace (without using #define)
3. allow constants to be whatever precision is needed for the app

我试图在同一名称空间中保留我的头文件库的所有常量。我决定废除#define并以正确的方式做事,例如使用:

namespace studio
{
    namespace constant
    {
        const char* WHITE_SPACE = "\n\r\t ";
    }
}

希望是这样访问: studio::constant::WHITE_SPACE

这一切都很好,从我收集到的,这样做为每个翻译单元创建其中一个,可能在链接期间优化到单个实例。即使它没有像那样进行优化,在这种情况下也可能没问题。

当我想添加除const char *之外的其他类型作为常量时,麻烦来了。例如,假设我想允许浮点类型(double或float)作为常量,但我想将其作为模板,因此我不必执行以下操作:

namespace studio
{
    namespace constant
    {
         const char* WHITE_SPACE = "\n\r\t ";
         const float PI_FLOAT = 3.141592653589793;
         const double PI_DOUBLE = 3.141592653589793;
    }
}

所以我尝试使用模板化的类而不是命名空间,静态函数返回静态常量,如下所示:

namespace studio
{
    template <class FloatType = float>
    class constant
    {
    public:
        static const char* white_space_chars() {
            static const char* whiteSpaceChars = "\n\r\t ";
            return whiteSpaceChars;
        }
        static const FloatType pi() {
            static const FloatType _pi = 3.141592653589793;
            return _pi;
        }
    }
}

然后以这种方式访问​​:studio::constant<float>::pi()

但是现在我遇到的问题是,如果我想要一个const chars,我必须提供一个模板参数,即使它们与浮点数无关,它们必须像这样访问: studio::constant<float>::white_space_chars() 因为 studio::constant::white_space_chars() 虽然我指定了一个默认的模板参数,显然只适用于类,但它不起作用。 C ++不允许在函数模板上使用默认值。

所以现在我能看到解决这个问题的唯一方法就是拥有studio :: constant_char,studio :: constant_float,studio :: constant_int等等。这太荒谬了吧?

我真正想要的就是在命名空间中使用我的定义......

你知道一个更好的方法来做我想做的事情,其中​​不同类型的变量可以使用模板,如在studio :: constant命名空间中,而不强迫库的用户指定每个模板参数的类型?最好不要使用C ++ 11来更向后兼容。

3 个答案:

答案 0 :(得分:1)

将模板移动到实际使用它的静态方法上:

namespace studio
{
    class constant
    {
    public:
        static const char* white_space_chars() {
            return "\n\r\t ";
        }

        template <class FloatType = float>
        static const FloatType pi() {
            return FloatType(3.141592653589793);
        }
    };
}

然后你可以这样做:

float flt_pi = studio::constant::pi();
double dbl_pi = studio::constant::pi<double>();

答案 1 :(得分:0)

我想这可以通过具有可覆盖的定义来完成,例如:

#ifndef STUDIO_FLOAT_TYPE
typedef float STUDIO_FLOAT_TYPE;
#endif

namespace studio
{
    namespace constant
    {
         const char* WHITE_SPACE = "\n\r\t ";
         const STUDIO_FLOAT_TYPE PI = 3.141592653589793;
    }
}

允许用户在包含库头之前覆盖float类型。至少只有一个定义,它仍然有默认值。

因为这样的定义肯定会在其他头文件中使用,我想将所有可自定义的定义保存在所有单独的库头所引用的单个头文件中可能会很好。

答案 2 :(得分:0)

为了将来参考,您可以在C ++ 14中执行以下操作:

template <typename T>
const T PI = T(3.141592653589793);

请参阅variable templates

举个例子:

// header.h
namespace constant
{
  template <typename T>
  constexpr T PI = T(3.141592653589793);
}

void foo();
// foo.cpp
#include <iostream>
#include "header.h"

void foo() {
    std::cout << constant::PI<int> << std::endl;
}
// main.cpp
#include <iostream>
#include "header.h"

int main()
{
  std::cout << constant::PI<float> << std::endl;
  foo();
}

这给出了输出:

3.14159
3