今天说两个关于编译期的小技巧。

看如下例子:

struct S {
    int val;
    constexpr int size() const {
        return val * (val + 1) / 2;
    }
};

constexpr auto bad(S s) {
    // doesn't compile
    return std::array<int, s.size()>{};
}

int main() {
    constexpr S s{42};
    auto my_array = bad(s);
}

这里 bad() 的参数 s 就称为 Conditional compile-time expression,它不能强保证在编译期执行,因为 main() 中的 s 可能不是 constexpr 的,因此 bad() 函数会编译失败。

为了让 bad() 函数一定在编译期执行,需要让其参数变为 Unconditional compile-time expression

有两种方式。

第一种方式,可以传递一个 lambda 作为 constexpr function 的参数,通过执行该 lambda 来得到一个constexpr value

代码为:

template <typename Callable>
constexpr auto good(Callable callable) {
    return std::array<int, callable().size()>{};
}

int main() {
    constexpr S s{42};
    constexpr auto make_compile_time_data = [] {
        return s;
    };
    auto my_array = good(make_compile_time_data);
}

这种方式我在 constexpr string 那篇文章中用过,这是一种不错的技巧。

但是你得额外提供一个 lambda 函数,若不想这么麻烦,可以采用第二种方式,这种方式是借助了NTTP

template <auto> struct compile_time_param {};
template <auto Data> inline auto compile_time_cast = compile_time_param<Data>{};

template <S s>
constexpr auto also_good(compile_time_param<s>) {
    return std::array<int, s.size()>{};
}

int main() {
    constexpr S s{42};
    auto my_array = also_good(compile_time_cast<s>);
}

NTTP 和 constexpr variables 有着一样的性质,采用这种方式也可以将 Conditional compile-time expression 转换为 Unconditional compile-time expression,提供强编译期保证。

这两个技巧在编译期编程中十分有用,可以用起来。

Leave a Reply

Your email address will not be published. Required fields are marked *

You can use the Markdown in the comment form.