以下代码在 g++ 9 下编译,带有标志-std=c++17,但不是带有相同标志的 clang 8:
#include <string_view>
#include <cstdlib>
int main() {
constexpr std::string_view hello = "hello";
constexpr size_t last_l = hello.find_last_of("lo");
}
Run Code Online (Sandbox Code Playgroud)
错误信息如下:
test.cpp:8:19: error: constexpr variable 'last_l' must be initialized by a constant expression
constexpr size_t last_l = hello.find_last_of("lo");
^ ~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/char_traits.h:349:9: note: cast from 'void *' is not allowed in a constant expression
return static_cast<const char_type*>(__builtin_memchr(__s, __a, __n));
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/string_view.tcc:150:12: note: in call to 'find(&"lo"[0], 2, "hello"[4])'
if (traits_type::find(__str, __n, this->_M_str[__size]))
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/string_view:402:22: note: …Run Code Online (Sandbox Code Playgroud) 受编译时计数函数参数的启发
考虑这个代码:
template <typename... Args>
constexpr int count(Args&&...)
{
return sizeof...(Args);
}
void foo(int value)
{
static_assert(count(value) >= 0); // OK
const int& ref = 7;
static_assert(count(ref) >= 0); // Error
}
Run Code Online (Sandbox Code Playgroud)
首先static_assert工作正常。第二个报错:
<source>:12:19: error: static_assert expression is not an integral constant expression
static_assert(count(ref) >= 0);
^~~~~~~~~~~~~~~
<source>:12:25: note: initializer of 'ref' is not a constant expression
static_assert(count(ref) >= 0);
^
<source>:11:16: note: declared here
const int& ref = 7;
^
Run Code Online (Sandbox Code Playgroud)
这两种情况都令我感到惊讶。为什么第一个static_assert工作正常,而 …
考虑一个具有常量成员的类:
class foo {
public:
const static int N;
};
Run Code Online (Sandbox Code Playgroud)
foo::N 需要初始化:
constexpr int foo::N = 5;
Run Code Online (Sandbox Code Playgroud)
并注意使用constexpr限定符而不是const这里似乎是一个错误。
但是 GCC、Clang 和 MSVC 都编译得很好!
Clang 甚至允许同时使用两个限定符版本:
constexpr int foo::N = 3;
const int foo::N = 5;
int main(){
return foo::N; //returns 3
}
Run Code Online (Sandbox Code Playgroud)
这是怎么回事?
我正在寻找一种在编译时将类型映射到数值的方法,理想情况下不使用本答案中建议的散列。
由于指针可以constexpr,我试过这个:
struct Base{};
template<typename T> struct instance : public Base{};
template<typename T>
constexpr auto type_instance = instance<T>{};
template<typename T>
constexpr const Base* type_pointer = &type_instance<T>;
constexpr auto x = type_pointer<int> - type_pointer<float>; // not a constant expression
Run Code Online (Sandbox Code Playgroud)
gcc 和 clang 都拒绝此代码,因为type_pointer<int> - type_pointer<float>它不是一个常量表达式,例如,请参见此处。
为什么?
我可以理解,从一个编译到下一个编译,这两个值之间的差异不会稳定,但在一个编译中,它应该是constexpr,恕我直言。
我正在试验 std::max。我想通过统一初始化(花括号)传递整数 constexpr,以将它们与浮点变量进行比较。
实验 a):用 double/int 混合调用 std::max()
double a = 3.0;
int b = 5;
auto res = std::max(a, b);
Run Code Online (Sandbox Code Playgroud)
不编译。铿锵报告error: no matching function for call to 'max'。这当然没问题。
实验 b):使用花括号 + constexpr int 进行非箭头转换
double a = 3.0;
constexpr int b = 5;
auto res = std::max(a, {b});
Run Code Online (Sandbox Code Playgroud)
编译并按预期工作:返回值为 5.0 的双精度值。
实验 c):与 b) 相同,但交换 std::max 的参数。
double a = 3.0;
constexpr int b = 5;
auto res = std::max({b}, a);
Run Code Online (Sandbox Code Playgroud)
不能在 gcc 和 clang 下编译。为什么?
铿锵报告 …
我有一个相对简单的用例:我想将一个特征关联到一个类,该类将返回一些用户定义的字符串,即一些用户定义的注册 ID。由于这个注册应该在编译时定义,我希望它是 constexpr,所以我写了如下内容:
template <typename T>
struct ClassRegistration
{
static constexpr std::string_view
Name();
};
template <>
struct ClassRegistration<int>
{
static constexpr std::string_view
Name()
{
return std::string_view{ "int" };
}
};
Run Code Online (Sandbox Code Playgroud)
一切正常,但由于 string_view 实际上并不拥有它的缓冲区,我想知道它是否保证安全,我不只是指一个悬空指针。从我读到的字符串文字保证具有与程序本身一样长的生命周期(从这个函数返回的字符串文字的SO Lifetime)。
因此,string_view 的这种用法是否安全合适?
我碰巧发现GCC和Clang在以下代码的编译上有所不同:
struct Foo
{
int mem = 42;
};
int main()
{
constexpr Foo foo;
static_assert(__builtin_constant_p(foo));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我用g++ -std=c++17和编译clang++ -std=c++17。
特别是,
g++-9 (Homebrew GCC 9.3.0_1) 9.3.0编译,而Apple clang version 11.0.3 (clang-1103.0.32.62)无法编译,抱怨说error: static_assert failed due to requirement '__builtin_constant_p(foo)'
static_assert(__builtin_constant_p(foo));
^ ~~~~~~~~~~~~~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)
我没有发现任何关于__builtin_constant_p.
为了 __builtin_constant_p
您可以使用内置函数 __builtin_constant_p 来确定某个值在编译时是否已知为常量...
叮当 说
Clang 支持许多与 GCC 语法相同的内置库函数,包括 __builtin_nan、__builtin_constant_p、__builtin_choose_expr、__builtin_types_compatible_p、__builtin_assume_aligned、__sync_fetch_and_add 等。
问题:虽然我知道__builtin_constant_p是一个编译器扩展,但哪个应该是正确的?
在该准则下constexpr everything,随着constevalC++20的引入,越来越多的代码在编译时被评估。
这导致了一个显而易见的问题:我们如何调试它?
目前唯一的提示是编译器错误。但是如果代码编译了,但仍然没有达到预期的效果怎么办。有什么工具可以帮助解决这个问题吗?有没有检查的可能?
一个相关的问题是:如何知道哪些会在编译时真正被“执行”,哪些会在有限定符的情况下保持运行。
由于未知原因,我无法从constexpr值初始化枚举值。这是我的代码:
enum class Enum: unsigned int; //Forward declaration
constexpr Enum constant = static_cast<Enum>(2);
enum class Enum: unsigned int {
A = 0,
B = 1,
C = B, //This works
D = constant, //This FAILS
E = static_cast<unsigned int>(constant), //This works
F = Enum::B //This works
};
Run Code Online (Sandbox Code Playgroud)
我无法理解的是为什么我可以写C = B,但不能写D = constant(B并且constant具有相同的类型!)
我仍然可以做E = static_cast<unsigned int>(constant),但它太冗长了(在我现实生活中的代码中,每个枚举值都是由constexpr函数调用初始化的,很难放在static_cast<unsigned int>任何地方)。
当我遇到问题时,我正在用工会做一些实验。
union U
{
// struct flag for reverse-initialization of each byte
struct rinit_t { };
constexpr static const rinit_t rinit{};
uint32_t dword;
uint8_t byte[4];
constexpr U() noexcept : dword{} { }
constexpr U(uint32_t x) noexcept : dword{x} { }
constexpr U(uint32_t x, const rinit_t&) noexcept : dword{}
{
U temp{x};
byte[0] = temp.byte[3];
byte[1] = temp.byte[2];
byte[2] = temp.byte[1];
byte[3] = temp.byte[0];
}
};
Run Code Online (Sandbox Code Playgroud)
这是我的示例实例:
constexpr U x{0x12345678, U::rinit};
Run Code Online (Sandbox Code Playgroud)
我在 g++ 的 5.1 和 8.1 版中遇到了这个错误-std=c++14,-std=c++17 …