自C++11起支持用户自定义字面量,用户定义字面量有如下:
字面量运算符
用户定义字面量所调用的函数被称为字面量运算符 ,形式为:operator ""标识符;
字面量运算符仅允许下列形参列表:
详细说明可以参见【1】;
用户可以自定义字面量进行单位转换或类型转换,例如:
struct mytype
{
unsigned long long m;
};
constexpr mytype operator""_km(unsigned long long n)
{
return {n};
}
mytype y = 123_km;
字面量运算符模板
如果字面量运算符是模板,那么它必须有空形参列表,并且只能有一个模板形参,模板形参必须是元素类型是 char 的非类型模板形参包(此时称之为数值字面量运算符模板):
template<char...>
double operator ""_x();
或类类型的非类型模板形参(此时称之为字符串字面量运算符模板):(C++20 起)
struct A { constexpr A(const char*); };
template<A a>
A operator ""_a();
这里延伸出如何借用“字面量运算符模板 ”将字符串字面量映射编译期的常量类型,hana库中提供了一种实现:
template<char ...Cs>
struct Str {
static constexpr bool isEmpty = false;
static constexpr char value[] = {Cs..., '\0'};
constexpr operator const char*(){ return value; }
};
template<>
struct Str<> {
static constexpr bool isEmpty = true;
};
template<typename T, T ...Cs>
constexpr Str<Cs...> operator""_S()
{
return {};
}
constexpr auto s = "hello world"_S;
using Type = decltype("hello world"_S); // 获取编译期字符串类型
但这种实现是Clang和Gcc的扩展实现【2】:
This form will be used if a non-template literal operator for the string literal is not available. The first template argument will be the element type of the string, and the remaining arguments are the code units in the string literal (excluding its terminating null character).
但此语法糖并非标准,因此会产生告警,在Hana中也进行了说明【3】:
Warning:This user-defined literal is an extension which requires a special string literal operator that is not part of the standard yet. That operator is supported by both Clang and GCC, and several proposals were made for it to enter C++17. However, since it is not standard, it is disabled by default and defining the BOOST_HANA_CONFIG_ENABLE_STRING_UDL
config macro is required to get this operator. Hence, if you want to stay safe, just use the BOOST_HANA_STRING
macro instead. If you want to be fast and furious (I do), define BOOST_HANA_CONFIG_ENABLE_STRING_UDL
.
Hana提供了另外一种实现:
namespace string_detail {
template <char ...s>
struct string {
static constexpr char string_storage[sizeof...(s) + 1] = {s..., '\0'};
};
template <typename S, std::size_t ...N>
constexpr string<S::get()[N]...>
prepare_impl(S, std::index_sequence<N...>)
{ return {}; }
template <typename S>
constexpr decltype(auto) prepare(S s) {
return prepare_impl(s,
std::make_index_sequence<sizeof(S::get()) - 1>{});
}
}
#define BOOST_HANA_STRING(s) \
(::string_detail::prepare([]{ \
struct tmp { \
static constexpr decltype(auto) get() { return s; } \
}; \
return tmp{}; \
}())) \
constexpr auto s = BOOST_HANA_STRING("hello world");
using Type = decltype(BOOST_HANA_STRING("hello world")); // 编译失败,因为不允许在decltype中定义类型
但是却无法使用decltype使用一行代码得到其类型;
C++20添加支持了 class template argument deduction ,即模板参数的类型推导【4】
template<class T>
struct X
{
constexpr X(T) {}
};
template<X x>
struct Y {};
Y<0> y; // OK, Y<X<int>(0)>, 可以推导非类型模板参数中的X模板参数为int
在提案【5】提供了参考实现:
template<auto size>
struct FixedString {
char data[size + 1]{};
constexpr FixedString(char const* str) { std::copy_n(str, size + 1, data); }
constexpr operator std::string_view() const { return {data, size}; }
};
template<auto size> FixedString(const char (&)[size]) -> FixedString<size - 1>;
template<FixedString str>
constexpr auto operator""_s() { return str; }
constexpr auto s = "hello world"_S;
using Type = decltype("hello world"_S); // 获取编译期字符串类型
参考资料
【1】https://en.cppreference.com/w/cpp/language/user_literal
【2】https://open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3599.html
【3】http://boostorg.github.io/hana/structboost_1_1hana_1_1string.html#a7f15faa77e126e1c2b08a2224ceefb84
【5】https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0732r2.pdf