许多开发人员和库作者已经在编译时字符串方面苦苦挣扎了好几年了,因为标准(库)字符串std::string需要动态分配内存,而不是constexpr。
因此,关于如何正确获取编译时字符串,我们有很多问题和博客文章:
现在我们已经了解到,不仅代码new可用constexpr,可以在编译时进行动态分配,而且实际上,std::string它将在C ++ 20(Herb Sutter的C ++标准工作组会议报告)中成为constexpr。
这是否意味着对于C ++ 20及以上版本的代码,我们应该放弃所有这些漂亮的编译时字符串实现,并且始终使用std::string?
如果不是,我们什么时候会这样做,什么时候我们会坚持今天的可能(当然不是向后兼容的代码)?
注意:我不是在谈论内容是其类型一部分的字符串,即不是在谈论std::integral_constant; 的等价形式。绝对不会std::string。
看起来我可以初始化一个POD静态const成员,但不是其他类型:
struct C {
static const int a = 42; // OK
static const string b = "hi"; // compile error
};
Run Code Online (Sandbox Code Playgroud)
为什么?
是否可以编写一个在编译时将任何 C 字符串文字转换"XY..."为类型的构造?std::integer_sequence<char, 'X', 'Y', ...>我真的想要一个角色包,而不是std::array.
就像是
using MyStringType = magic_const_expr("MyString");
Run Code Online (Sandbox Code Playgroud)
这里演示了一种使用文字运算符的好方法,但不幸的是它需要非标准编译器扩展。
我正在尝试使用模板来创建type_info::name()发出const限定名称的函数的模拟.例如typeid(bool const).name(),"bool"但我想看"bool const".所以对于泛型类型我定义:
template<class T> struct type_name { static char const *const _; };
template<class T> char const *const type_name<T>::_ = "type unknown";
char const *const type_name<bool>::_ = "bool";
char const *const type_name<int>::_ = "int";
//etc.
Run Code Online (Sandbox Code Playgroud)
然后type_name<bool>::_是"bool".对于非const类型,显然我可以为每种类型添加一个单独的定义,等等char const *const type_name<bool const>::_ = "bool const";.但是我想我会尝试使用部分特化和连接宏来在一行中派生任何类型的const限定名称const - 先前定义的资格名称.所以
#define CAT(A, B) A B
template<class T> char const *const type_name<T const>::_
= CAT(type_name<T>::_, " const"); // …Run Code Online (Sandbox Code Playgroud) 我想在编译时实现 atoi() 函数(在 C++ 语言中,使用 C++11 或 C++14 标准)。所以它应该能够将双引号括起来的文本解析为数字,或者报告错误。更具体地说,它是更大系统的一部分,能够在编译时解析类似 printf 的格式。而且我想在单词上拆分格式字符串,如果某些特定单词可以用数字表示 - 输出数字而不是字符串(在场景后面是序列化器类,它可以比字符串更有效地序列化数字,哪个更重要的是,反序列化器不应该尝试将每个字符串解析为数字,因为格式字符串中打印的所有数字始终表示为数字,而不是字符串)...
据我所知,有两种方法可以解决任务:
1) 通过使用 constexpr 函数;
2)通过模板元编程。
哪种方式可以更好?我尝试了第一种方式,我可以看到这种方式存在很多障碍:尤其是与 c++11 相关的限制很少。看起来第二个可能更可取,但它需要一些技巧(您需要使用运算符“”来拆分 c 字符串以分隔字符,这在从 c++14 开始的 gcc 和从 c++11 开始的 clangs 中支持)。此外,完全基于 TMP 的解决方案可能太大且太纠结。
以下是我的解决方案,我很高兴听到一些关于它的建议。
http://coliru.stacked-crooked.com/a/0b8f1fae9d9b714b
#include <stdio.h>
template <typename T> struct Result
{
T value;
bool valid;
constexpr Result(T v) : value(v), valid(true) {}
constexpr Result() : value(), valid(false) {}
};
template <typename T>
constexpr Result<T> _atoi_oct(const char *s, size_t n, T val, int sign)
{
return n == …Run Code Online (Sandbox Code Playgroud) c++ compile-time-constant compile-time template-meta-programming c++11
我正在为我公司的C++项目添加编译时检查,以确保所有开发机器和构建服务器上的第三方库都是最新的.大多数库定义类似于以下内容的版本,例如版本3.1.4:
#define VERSION_MAJOR 3
#define VERSION_MINOR 1
#define VERSION_BUILD 4
Run Code Online (Sandbox Code Playgroud)
这很容易检查使用static_assert或预处理器指令.
现在我正在查看定义单个宏的第三方库:
#define VERSION 3.1.4
Run Code Online (Sandbox Code Playgroud)
如何在编译时验证这样一个宏的值?
使用C++ 11,我可以使用constexpr字符串比较函数,并将宏字符串化以检查它:
constexpr bool static_equal(const char * a, const char * b)
{
return (*a == *b) && (*a == '\0' || static_equal(a + 1, b + 1));
}
// stringification functions
#define str(x) #x
#define xstr(x) str(x)
static_assert(static_equal(xstr(VERSION), "3.1.4"), "incorrect version of libwhatever");
Run Code Online (Sandbox Code Playgroud)
但我们在Windows机器上使用Visual Studio 2013,因此我只能使用它支持的C++ 11子集.不幸的constexpr是不支持.
我想创建一个我可以用作模板参数的字符串文字.它将编译器抛入某种无限循环.有什么问题并修复?
template <char...> struct slit { };
template <typename ...A>
constexpr auto make_slit(char const* const s, A const ...args)
{
return *s ? make_slit(s + 1, *s, args...) : slit<args...>();
}
int main()
{
auto const tmp_(make_slit("slit"));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
强制性错误(带clang++ -std=c++1y):
t.cpp:4:16: fatal error: recursive template instantiation exceeded maximum depth of 256
constexpr auto make_slit(char const* const s, A const ...args)
^
t.cpp:6:15: note: in instantiation of function template specialization 'make_slit<char, char, char, char, char, char, char, …Run Code Online (Sandbox Code Playgroud) 我正在尝试在编译时将月份从__DATE__格式转换Mmm为数字.我需要它用于gcc和MS VC 2012.
我的尝试:
template <char C0, char C1, char C2>
struct month{};
template<> struct month < 'J', 'a', 'n'> { static const unsigned id = 1; };
template<> struct month < 'S', 'e', 'p'> { static const unsigned id = 9; };
static const char c0 = __DATE__[0];
static const char c1 = __DATE__[1];
static const char c2 = __DATE__[2];
static const unsigned currId = month<c0, c1, c2>::id; //this gives error
static const unsigned currId2 …Run Code Online (Sandbox Code Playgroud) 目前我有:
template <typename T> struct typename_struct<T*> {
static char const* name() {
return (std::string(typename_struct<T>::name()) + "*").c_str();
}
};
Run Code Online (Sandbox Code Playgroud)
我想知道我是否可以避免在我被迫分配字符串来执行连接的整个位置.
这一切都发生在编译时,即我打算"int****"在引用时获取字符串typename_struct<int****>::name().(假设我已经声明了相应的int返回专门化"int")
在编写代码时,编译器是否仅在编译时使用std :: string进行串联?(我会好的)或者这样的调用会在运行时导致基于4 std :: string的连接吗?(我不会那么好)
提案n4121看起来好像要添加一个std::string_literal类型.它包含如下代码:
template<size_t n> struct string_literal { char data [n]; }
Run Code Online (Sandbox Code Playgroud)
和:
template <size_t N>
constexpr int stoi( const string_literal<N>& str,
size_t* idx = 0, int base = 10);
Run Code Online (Sandbox Code Playgroud)
这是我的Number类:
template <typename T>
struct Number
{
private:
T t;
public:
constexpr Number(const T& t)
: t(t)
{
}
constexpr Number(const std::string& s);
constexpr operator T() const
{
return t;
}
};
template <>
constexpr Number<int>::Number(const std::string& s)
: t(std::stoi(s))
{
}
Run Code Online (Sandbox Code Playgroud)
我已经看过如何在编译时将C字符串转换为int?但它只适用于C字符串.并且c_str()是非constexpr.另外最重要的是,这并不能掩盖stol …
我的C++代码中有一些带有绑定的SQL查询,这些查询是static const std::string,因为这些查询很复杂,所以很容易出错.我想在编译时做一些非常基本的检查,例如计算逗号或:字符的数量.
constexpr std::tuple<int,char,int,char> t1 = parse("1a2b");
constexpr std::tuple<int,int,int,char> t2 = parse("123a");
constexpr std::tuple<char,int> t3 = parse("a2");
Run Code Online (Sandbox Code Playgroud)
这样的事情会成为可能吗?
我对TMP并不完全流利,但我从以下开始
template<std::size_t N,typename ...Args> constexpr
std::tuple<Args...> parse(const char* s[N]){
constexpr char c = s[0];
constexpr char* new_s[N-1] = "";
for (int i = 1; i < N; i++) {
new_s[i] = s[i];
}
//my own constexpr isdigit
if(!isdigit(c)){
return parse(new_s,c);
}
else if(isdigit(c)){
return parse(new_s,static_cast<int>(c));
}
throw std::invalid_argument("");
}
...
Run Code Online (Sandbox Code Playgroud)
我想以递归方式执行此操作并累积元组,但我很快意识到每次传入新元组时类型都会改变.
我们来看一个简单的SFINAE模板示例
#include <iostream>
template <typename T>
struct has_typedef_foobar {
// Types "yes" and "no" are guaranteed to have different sizes,
// specifically sizeof(yes) == 1 and sizeof(no) == 2.
typedef char yes[1];
typedef char no[2];
template <typename C>
static yes& test(typename C::foobar*);
template <typename>
static no& test(...);
// If the "sizeof" of the result of calling test<T>(0) would be equal to sizeof(yes),
// the first overload worked and T has a nested type named foobar.
static const bool value …Run Code Online (Sandbox Code Playgroud)