### 一、编译期计算

size_t factorial(size_t n) noexcept
{
return (n == 0) ? 1 : n * factorial(n - 1);
}

int main(void)
{
std::cout << factorial(10) << std::endl;
return 0;
}

template <size_t N>
struct factorial
{
enum : size_t { value = N * factorial<N - 1>::value };
};

template <>
struct factorial<0>
{
enum : size_t { value = 1 };
};

int main(void)
{
std::cout << factorial<10>::value << std::endl;
return 0;
}

### 二、C++11以后的新限定符：constexpr

template <size_t N>
struct t_factorial_
{
enum : size_t { value = N * t_factorial_<N - 1>::value };
};

template <>
struct t_factorial_<0>
{
enum : size_t { value = 1 };
};

template <size_t N>
constexpr auto t_factorial = t_factorial_<N>::value;

int main(void)
{
std::cout << t_factorial<10> << std::endl;
return 0;
}

constexpr size_t c_factorial(size_t n) noexcept
{
return (n == 0) ? 1 : n * c_factorial(n - 1);
}

// runtime version
template <typename T, size_t N>
size_t r_count(T&& v, const T(&arr)[N]) noexcept
{
size_t r = 0;
for (const auto& a : arr) if (v == a) ++r;
return r;
}

// constexpr version
template <typename T, size_t N>
constexpr size_t c_count(T&& v, const T(&arr)[N]) noexcept
{
size_t r = 0;
for (const auto& a : arr) if (v == a) ++r;
return r;
}

constexpr同样带来了强大的类型计算能力。我们简单的来看个例子，实现一个“types_insert”（Reference：C++的杂七杂八：使用模板元编程操作类型集合）：

template <typename...>
struct types {};

template <typename T, typename... U>
constexpr auto insert(types<U...>) noexcept
{
return types<T, U...>{};
}

template <int N, typename T>
struct impl_
{
constexpr static auto assign(void) noexcept
{
return insert<T>(impl_<N - 1, T>::assign());
}
};

template <typename T>
struct impl_<0, T>
{
constexpr static auto assign(void) noexcept
{
return types<>{};
}
};

template <int N, typename T>
constexpr auto assign(void) noexcept
{
return impl_<N, T>::assign();
}

template <size_t N>
struct Foo { enum : size_t { value = N }; };

int main(void)
{
std::cout << Foo<c_count(',', "1, 2, 3, 4, 5, 6, 7, 8, 9, 0")>::value << std::endl;
return 0;
}

### 三、constexpr性能实测

// https://github.com/mutouyun/capo/blob/master/capo/stopwatch.hpp
#include "stopwatch.hpp"

size_t r_fib(size_t n) noexcept
{
if (n == 0) return 0;
if (n == 1) return 1;
return r_fib(n - 1) + r_fib(n - 2);
}

constexpr size_t c_fib(size_t n) noexcept
{
if (n == 0) return 0;
if (n == 1) return 1;
return c_fib(n - 1) + c_fib(n - 2);
}

template <size_t N> struct t_fib_ { enum : size_t { value = t_fib_<N - 1>::value + t_fib_<N - 2>::value }; };
template <> struct t_fib_<0> { enum : size_t { value = 0 }; };
template <> struct t_fib_<1> { enum : size_t { value = 1 }; };
template <size_t N>
constexpr auto t_fib = t_fib_<N>::value;

////////////////////////////////////////////////////////////////

int main(void)
{
constexpr size_t n = 40;
capo::stopwatch<> sw;

sw.start();
size_t r1 = r_fib(n);
std::cout << r1 << ": " << sw.elapsed<std::chrono::milliseconds>() << "ms" << std::endl;

sw.start();
size_t r2 = c_fib(n);
std::cout << r2 << ": " << sw.elapsed<std::chrono::milliseconds>() << "ms" << std::endl;

sw.start();
size_t r3 = t_fib<n>;
std::cout << r3 << ": " << sw.elapsed<std::chrono::milliseconds>() << "ms" << std::endl;

return 0;
}

g++ -v：version 5.3.1 20160413 (Ubuntu 5.3.1-14ubuntu2)

102334155: 363ms
102334155: 364ms
102334155: 0ms

sw.start();
const size_t r2 = c_fib(n);
std::cout << r2 << ": " << sw.elapsed<std::chrono::milliseconds>() << "ms" << std::endl;

template <size_t N>
struct Foo { enum : size_t { value = N }; };

size_t xx = Foo<c_fib(40)>::value;

char cc[c_fib(n)];

constexpr并不会尽可能的让expressions在编译期执行，只是表示expressions能在编译期执行。至于真正执行的时机是编译期还是运行期，依赖编译器自己的选择。见constexpr specifier (since C++11)

The constexpr specifier declares that it is possible to evaluate the value of the function or variable at compile time. Such variables and functions can then be used where only compile time constant expressions are allowed (provided that appropriate function arguments are given).

constexpr size_t test(size_t n) noexcept
{
return Foo<n>::value; // error: ‘n’ is not a constant expression
}

const size_t xx = test(123);

constexpr size_t cube(size_t n) noexcept
{
constexpr size_t cp = c_power(n, 3); // error: ‘n’ is not a constant expression
return cp;
}

• 除了语法上的必须之外（比如在模板参数上使用函数），constexpr函数是否在编译期执行，取决于编译器的选择；
• 不论该函数是否在编译期执行，constexpr函数里，参数都只是普通的变量。

C++这样设计估计是考虑到constexpr函数需要同时具备编译期和运行期执行的能力吧。如果我们需要在constexpr函数内使用编译期参数，只能使用template non-type arguments了。

### 四、constexpr的应用：实现一个编译期字符串split

constexpr可以用在构造函数上，这样定义的类可以用来定义constexpr对象，像这样：

struct Foo
{
int i_ = 0;
constexpr Foo(void) noexcept {}
};

template <int>
struct Bar {};

constexpr Foo foo;
Bar<foo.i_>{};

namespace literal {

template <size_t N>
class string
{
char str_[N] = {};

public:
constexpr string(void) noexcept = default;
constexpr string(const char* str, size_t n) noexcept
{
for (size_t i = 0; i < std::min(N, n); ++i)
str_[i] = str[i];
}

template <size_t N_>
constexpr string(const char(& str)[N_]) noexcept
: string{ str, N_ }
{}

constexpr auto operator[](size_t n) const noexcept
{
if (n >= N) return '\0';
return str_[n];
}

constexpr size_t size(void) const noexcept
{
return N;
}

constexpr size_t length(void) const noexcept
{
size_t r = 0;
for (; r < N; ++r)
if (str_[r] == '\0') break;
return r;
}

constexpr auto begin(void) const noexcept
{
return str_;
}

constexpr auto end(void) const noexcept
{
return str_ + length();
}

constexpr const char* c_str(void) const noexcept
{
return str_;
}

constexpr size_t count(char c) const noexcept
{
size_t r = 0;
for (size_t i = 0; i < length(); ++i)
if (c == str_[i]) ++r;
return r;
}

static const size_t npos = -1;

constexpr string substr(size_t pos, size_t len = -1) const noexcept
{
return { str_ + pos, std::min(length() - pos, len) };
}

constexpr size_t find(char c, size_t pos = 0) const noexcept
{
for (size_t i = pos; i < length(); ++i)
if (c == str_[i]) return i;
return npos;
}
};

} // namespace literal

namespace literal {

template <size_t N>
constexpr string<N> make(const char(& str)[N]) noexcept
{
return { str };
}

} // namespace literal

constexpr auto str = literal::make("1,2,3,4,5");

namespace literal {

template <size_t M, size_t N>
class string_array
{
typedef string<N> string_t;
string_t arr_[M];

public:
constexpr string_array(void) noexcept = default;

constexpr const string_t& operator[](size_t n) const noexcept
{
return arr_[n];
}

constexpr const string_t* begin(void) const noexcept
{
return arr_;
}

constexpr const string_t* end(void) const noexcept
{
return arr_ + M;
}
};

} // namespace literal

namespace literal {

template <size_t M, size_t N>
class string_array
{
typedef string<N> string_t;
string_t arr_[M];

template <size_t M_, size_t N_>
friend constexpr string_array<M_, N_> split(char, const char(&)[N_]) noexcept;

// ...
};

template <size_t M, size_t N>
constexpr string_array<M, N> split(char delimiter, const char(& str)[N]) noexcept
{
string_array<M, N> r;
auto s = make(str);
size_t start = 0, end = s.find(delimiter);
for (size_t i = 0; i < M; ++i)
{
r.arr_[i] = s.substr(start, end - start);
if (end == string<N>::npos) break;
start = end + 1;
end = s.find(delimiter, start);
}
return r;
}

} // namespace literal

#define LITERAL_SPLIT(C, S) literal::split<literal::make(S).count(C) + 1>(C, S)

int main(void)
{
constexpr auto arr = LITERAL_SPLIT(',', "1,2,3,4,5");
for (auto& s: arr)
std::cout << s.c_str() << std::endl;

return 0;
}

### 五、使用constexpr简化类型计算

////////////////////////////////////////////////////////////////
// Define the literal string & helper types

template <char...>
struct literal_string { static const char to_string[]; };

template <char... S>
const char literal_string<S...>::to_string[] = { S... };

template <class... LS>
struct literal_array { static const std::array<std::string, sizeof...(LS)> to_array; };

template <class... LS>
const std::array<std::string, sizeof...(LS)> literal_array<LS...>::to_array = { LS::to_string... };

literal_string<"123"[0], "123"[1], "123"[2]>{};

#define CAPO_PP_MAX_                        256
#define CAPO_PP_VA_(...)                    __VA_ARGS__ /* Try to expand __VA_ARGS__ */
#define CAPO_PP_PROXY_(F, ...)              CAPO_PP_VA_(F(__VA_ARGS__))

#define CAPO_PP_REPEAT_0_(F1, F2, ...)
#define CAPO_PP_REPEAT_1_(F1, F2, ...)      CAPO_PP_VA_(F1(1, __VA_ARGS__))
#define CAPO_PP_REPEAT_2_(F1, F2, ...)      CAPO_PP_REPEAT_1_(F1, F2, __VA_ARGS__) CAPO_PP_VA_(F2(2, __VA_ARGS__))
......
#define CAPO_PP_REPEAT_256_(F1, F2, ...)    CAPO_PP_REPEAT_255_(F1, F2, __VA_ARGS__) CAPO_PP_VA_(F2(256, __VA_ARGS__))

/*
CAPO_PP_REPEAT(5, f, data)
-->
f(1, data) f(2, data) f(3, data) f(4, data) f(5, data)
*/

#define CAPO_PP_REPEAT_P_(F, ...)           CAPO_PP_VA_(F(__VA_ARGS__))
#define CAPO_PP_REPEATEX_(N, F1, F2, ...)   CAPO_PP_REPEAT_P_(CAPO_PP_JOIN_(CAPO_PP_REPEAT_, CAPO_PP_JOIN_(N, _)), F1, F2, __VA_ARGS__)
#define CAPO_PP_REPEATEX_MAX_(F1, F2, ...)  CAPO_PP_REPEATEX_(CAPO_PP_MAX_, F1, F2, __VA_ARGS__)
#define CAPO_PP_REPEAT_(N, F, ...)          CAPO_PP_REPEATEX_(N, F, F, __VA_ARGS__)
#define CAPO_PP_REPEAT_MAX_(F, ...)         CAPO_PP_REPEATEX_MAX_(F, F, __VA_ARGS__)

//////////////////////////////////////////////////////////////////////////

/* at */

template <size_t N>
constexpr auto at(size_t n, const char(&str)[N]) noexcept
{
return (n < N) ? str[n] : '\0';
}

////////////////////////////////////////////////////////////////
// Preprocessor

#define LITERAL_C(N, STR)       , at(N, STR)
#define LITERAL_S(STR)          literal_string<at(0, STR) CAPO_PP_REPEAT_MAX_(LITERAL_C, STR)>{}

////////////////////////////////////////////////////////////////
// Operations

template <char... S1, char... S2>
{
return literal_string<S1..., S2...>{};
}

template <typename... LS1, typename... LS2>
{
return literal_array<LS1..., LS2...>{};
}

/* insert */

template <bool Valid, char C, char... S>
constexpr auto insert(literal_string<S...>) noexcept
{
return typename std::conditional<Valid, literal_string<C, S...>, literal_string<S...>>::type{};
}

/* remove */

template <char V>
constexpr auto remove(literal_string<>) noexcept
{
return literal_string<>{};
}

template <char V, char C, char... S>
constexpr auto remove(literal_string<C, S...>) noexcept
{
return insert<V != C, C>(remove<V>(literal_string<S...>{}));
}

/* replace */

template <char V1, char V2>
constexpr auto replace(literal_string<>) noexcept
{
return literal_string<>{};
}

template <char V1, char V2, char C, char... S>
constexpr auto replace(literal_string<C, S...>) noexcept
{
return insert<true, (V1 == C) ? V2 : C>(replace<V1, V2>(literal_string<S...>{}));
}

/* count */

template <char V>
constexpr size_t count(literal_string<>) noexcept
{
return 0;
}

template <char V, char C, char... S>
constexpr size_t count(literal_string<C, S...>) noexcept
{
return ((V == C) ? 1 : 0) + count<V>(literal_string<S...>{});
}

/* find */

constexpr size_t npos = static_cast<size_t>(-1);

template <char V, size_t N = 0>
constexpr size_t find(literal_string<>) noexcept
{
return npos;
}

template <char V, size_t N = 0, char C, char... S>
constexpr size_t find(literal_string<C, S...>) noexcept
{
return (V == C) ? N : find<V, N + 1>(literal_string<S...>{});
}

/* substr */

template <size_t B, size_t E = npos, size_t N = 0>
constexpr auto substr(literal_string<>) noexcept
{
return literal_string<>{};
}

template <size_t B, size_t E = npos, size_t N = 0, char C, char... S>
constexpr auto substr(literal_string<C, S...>) noexcept
{
return insert<(B <= N) && (N < E), C>(substr<B, E, N + 1>(literal_string<S...>{}));
}

// split

#if defined(_MSC_VER)
template <char V, class LS> struct find_ { enum: size_t { value = find<V>(LS{}) }; };
template <char Sep, class LS, size_t = find_<Sep, LS>::value> // VS2015对constexpr函数的支持不够好，不能直接把constexpr函数作为默认参数
#else
template <char Sep, class LS, size_t = find<Sep>(LS{})>
#endif
struct split_;

template <char Sep>
struct split_<Sep, literal_string<>, npos>
{
using type = literal_array<>;
};

template <char Sep, char... S>
struct split_<Sep, literal_string<S...>, npos>
{
using type = literal_array<literal_string<S...>>;
};

template <char Sep, size_t Pos, char... S>
struct split_<Sep, literal_string<S...>, Pos>
{
using head = decltype(substr< 0, Pos>(literal_string<S...>{}));
using tail = decltype(substr<Pos + 1>(literal_string<S...>{}));
};

template <char Sep, char... S>
constexpr auto split(literal_string<S...>) noexcept
{
return typename split_<Sep, literal_string<S...>>::type{};
}

////////////////////////////////////////////////////////////////
// Preprocessor

#define LITERAL_C(N, STR)       , at(N, STR)
#define LITERAL_S(STR)          substr<0, sizeof(STR)>(literal_string<at(0, STR) CAPO_PP_REPEAT_MAX_(LITERAL_C, STR)>{})
#define LITERAL_SPLIT(SEP, STR) decltype(split<SEP>(LITERAL_S(STR)))::to_array

auto arr = LITERAL_SPLIT(',', "1,2,3,4,5,6,7,8,9,0"); // std::array<std::string, 10>

#### 关于std数组和内置数组初始化的疑惑

2016-06-13 19:14:20

#### C++17 之 "constexpr if"

2018-04-18 08:24:19

#### c++中的constexpr与inline

2015-04-06 18:15:49

#### (C++)使用模板在编译期计算阶乘

2016-05-28 17:55:14

#### C/C++ 使用宏在编译期计算可变参数个数

2014-09-16 11:22:09

#### C++11中的有趣的新特性(constexpr ) (Range-based for loop)

2014-11-04 23:22:27

#### 常量表达式与const和constexpr

2015-03-28 15:20:03

#### C++总结：C++中的const和constexpr

2016-05-21 23:00:34

#### c++的const和constexpr

2016-10-13 14:13:46

#### C++Primer笔记

2016-10-13 23:36:10

## 不良信息举报

C++的杂七杂八：constexpr与编译期计算