3

不久前,我对“参数化”用户定义的文字有了一个想法,并且想知道在当前的 C++ 标准中是否有任何方法可以做到这一点。

基本上,这个想法是有一个用户定义的文字,它的行为可以根据一些参数进行调整。作为一个简单的例子,我选择了一个“定点”文字,它将浮点数转换为整数;该参数是小数位数方面的精度。

现在这只是一个练习,因为我不确定这在实际应用程序中如何或是否有用。

我的第一个想法是这样的:

namespace fp_impl {
    constexpr int floor(long double n) {
        return n;
    }

    constexpr int pow10(int exp) {
        return exp == 0 ? 1 : 10 * pow10(exp - 1);
    }

    template<int i>
    constexpr int fixed_point(long double n) {
        return floor(n * pow10(i));
    }

    namespace fp2 {
        constexpr int operator"" _fp (long double n) {
            return fixed_point<2>(n);
        }
    }

    namespace fp4 {
        constexpr int operator"" _fp (long double n) {
            return fixed_point<4>(n);
        }
    }
}

template<int prec> struct fp;
template<> struct fp<2> {
    namespace lit = fp2;
};
template<> struct fp<4> {
    namespace lit = fp4;
};

int main() {
    {
        using namespace fp<2>::lit;
        std::cout << 5.421_fp << std::endl; // should output 542
    }
    {
        using namespace fp<4>::lit;
        std::cout << 5.421_fp << std::endl; // should output 54210
    }
}

但是,它不会编译,因为在类范围内不允许命名空间别名。(它还要求您手动定义每个版本的operator"" _fp.)所以我决定尝试使用宏:

namespace fp {
    namespace detail {
        constexpr int floor(long double n) {
            return n;
        }

        constexpr int pow10(int exp) {
            return exp == 0 ? 1 : 10 * pow10(exp - 1);
        }

        template<int i>
        constexpr int fixed_point(long double n) {
            return floor(n * pow10(i));
        }
    }
}

#define SPEC(i) \
    namespace fp { \
        namespace precision##i { \
            constexpr int operator"" _fp(long double n) { \
                return fp::detail::fixed_point<i>(n); \
            } \
        } \
    }
SPEC(2); SPEC(4);
#undef SPEC
#define fp_precision(i) namespace fp::precision##i

int main() {
    {
        using fp_precision(2);
        std::cout << 5.421_fp << std::endl;
    }
    {
        using fp_precision(4);
        std::cout << 5.421_fp << std::endl;
    }
}

这是可行的,尽管它仍然需要为SPEC()您想要使用的每个精度使用宏。当然,对于从 0 到 100 的每个值,可以使用一些预处理器技巧来执行此操作,但我想知道是否有更像模板解决方案的东西,每个都在需要时被实例化。我对在模板类中使用声明为友元函数的运算符“”有一个模糊的想法,尽管我怀疑这也行不通。

作为说明,我确实尝试过template<int i> constexpr int operator"" _fp(long double n),但似乎这不是文字运算符的允许声明。

4

3 回答 3

6

operator()(int)您可以返回从文字运算符重载的类类型。然后你可以写

5.421_fp(2);
于 2012-07-06T22:04:14.517 回答
2

用户定义的文字函数将文字本身作为其唯一参数。您可以在函数外部使用状态,例如使用全局或线程局部变量,但这不是很干净。

如果参数将始终是编译时常量,并且它是数字的一部分,请将其传递给文字本身。这需要编写一个operator "" _ ( char const *, std::size_t )重载或template< char ... > operator "" _ ()模板并完全自己解析数字。

但是,您必须将这样的参数用于现有的浮点语法。尽管 C++ 定义了一个非常开放的预处理数字结构,但用户定义的文字必须由附加了ud 后缀标识符的有效标记构成。

您可能会考虑使用字符串而不是数字,但随后模板选项就消失了。

于 2012-07-05T10:17:40.080 回答
0

不需要宏来解决问题。由于问题涉及处理文字数字(例如,整数或浮点格式的数字),因此可以使用文字运算符的模板定义和模板元编程在编译时完成这项工作。

要进行定点文字转换,您可以使用带有 unsigned long long 的整数文字运算符,例如,

some_type operator "" _fp(unsigned long long num)
{
  // code
}

(或可能会损失精度的 long double )但这会导致一切都在运行时发生。

第 3 和第 4 节中的第 2.14.8 节(用户定义的 Lierals [lex.ext])中的 C++11 定义了文字运算符变体,包括整数浮点文字的模板版本!不幸的是,第 5 和第 6 段没有字符串字符文字定义模板版本。这意味着该技术仅适用于整数和浮点字面量。

因此,从 C++11 第 2.14.8 节开始,上面的 _fp 文字运算符可以改为:

template <char... Digits>
constexpr some_type operator "" _fp()
{
  return process_fp<2, Digits...>::to_some_type();
}

例如,其中 2 是int i来自 OP 的模板参数的值,并且some_type是返回类型需要的任何值。请注意,模板参数是一个char-- 而不是一个int或其他数字。另请注意,文字运算符没有参数。因此Digit - '0',需要类似代码来将数值转换为该字符的整数值。此外,Digits...将按从左到右的顺序进行处理。

现在可以使用模板元编程,process_fp其前向声明如下所示:

template <int i, char... Digits>
struct process_fp;

并且会static constexpr调用一个方法to_some_type()来计算并返回所需的编译时结果。

人们可能还想要一个有意义的、简单的例子。去年我写了这样使用的代码(下面的链接):

int main()
{
  using namespace std;

  const unsigned long long bits =
    11011110101011011011111011101111_binary;
  cout << "The number is: " << hex << bits << endl;
}

将二进制数 11011110101011011011111011101111 转换unsigned long long为编译时的二进制数并将其存储为位。使用上面提到的模板元编程技术的完整代码和解释在我的博客文章中提供,标题为Using The C++ Literal Operator

于 2012-07-09T07:18:29.933 回答