请考虑以下代码:
template<typename T>
constexpr inline T fma(T a, T b, T c)
{
return a * b + c;
}
Run Code Online (Sandbox Code Playgroud)
编译得很好.但为什么呢?理论上,constexpr函数只能调用其他constexpr函数.但是,不能保证运营商将是constexpr功能.例如,假设我有一些带有以下界面的类型:
class someType
{
someType operator + (const someType &rhs);
someType operator * (const someType &rhs);
};
Run Code Online (Sandbox Code Playgroud)
运算符+和*不是 constexpr.如果我写下面的代码:
fma(someType(), someType(), someType());
Run Code Online (Sandbox Code Playgroud)
它应该无法编译,因为constexpr函数调用非constexpr函数.但它编译得很好.为什么是这样?
我正在使用MinGW的G ++编译器和-std = c ++ 0x选项.
更新:我下面贴我自己的答案 在这里,还有这件事情更长的版本:http://scrupulousabstractions.tumblr.com/post/38460349771/c-11-type-safe-use-of-integer-user-defined-文字
题:
我做了一个简单的constexpr用户定义的文字_X是可以获得价值为unsigned long long(这是字面定义用户如何工作的数字:http://en.cppreference.com/w/cpp/language/user_literal),然后我做确保该值适合签名的长整数.
这一切都运行良好(太大的值导致编译错误),但只有当我明确创建一个变量,如
constexpr auto a= 150_X;
Run Code Online (Sandbox Code Playgroud)
相反,我会写一些典型的东西
cout << 150_X << endl;;
Run Code Online (Sandbox Code Playgroud)
测试不在编译时执行.
constexpr函数是否仅在编译时执行,如果它们被分配给constexpr变量?(我在标准中找不到)
是否有可能实现_X我正在寻找的安全行为?
完整示例:
#include<iostream>
#include<stdexcept>
inline constexpr long long testConv(unsigned long long v) {
return (v > 100 ) ? throw std::exception() : v;
} // will eventually use actual limit from numeric_limits
inline constexpr long long operator "" _X(unsigned long long f) {
return testConv(f) ;
}
int main(){
constexpr auto a= …Run Code Online (Sandbox Code Playgroud) 使用constexpr,可以在编译时或运行时根据参数计算函数.但通常,算法必须在编译时和运行时之间有所不同.例如.考虑constexpr版本的factorial.
constexpr int fact(int n)
{
return (n)?n*fact(n-1):1;
}
Run Code Online (Sandbox Code Playgroud)
如果n在运行时发生,那么函数效率低于一个forloop?是否有一些模板魔术来确定函数是在编译时还是在运行时执行并使用不同的算法?
更新:
factorial只是一个例子.constexpr如果编码没有constexpr限制,所有功能是否都有效?例如:
constexpr int combinations(int n, int k)
{
//Assume all error conditions and edge conditions are taken care with ternary operator ?:
return fact(n)/(fact(k)*fact(n-k);
}
Run Code Online (Sandbox Code Playgroud)
如果函数是在运行时编写的,那么它可以从Memoization中受益.即使这是可能的,我想很难表达这个功能,使其constexpr在运行时尽可能高效.
我有以下示例代码,它使用字符串文字作为模板参数,以便基类模板可以访问该字符串.
代码编译,但我得到一个我不完全理解的警告:
警告:'ns :: bar :: type'有一个基数'ns :: base <((const char*)(&ns :: bar :: name))>',其类型使用匿名命名空间[默认启用]
下面的工作示例代码:
// "test.h"
#pragma once
namespace ns
{
template <char const* str>
struct base
{
const char *name() const { return str; }
};
namespace bar
{
static constexpr char name[] = "bar";
struct type : base<name> {}; // <-- this line here
}
}
// main.cpp
#include <iostream>
#include "test.h"
int main()
{
ns::bar::type f;
std::cout << f.name() << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
所以我的问题是: …
constexpr uint32_t BitPositionToMask(int i,int Size){
static_assert(i < Size,"bit position out of range");
return 1 << i;
}
Run Code Online (Sandbox Code Playgroud)
这会产生:
error: non-constant condition for static assertion
Run Code Online (Sandbox Code Playgroud)
在GCC 4.6.2我没有得到什么或这是一个GCC错误?
更新: 谢谢Andy再次成为我的书呆子守护天使.因为我在编译时有值,所以我只是把它作为模板,它按预期工作.
template<int i,int Size>
constexpr uint32_t BitPositionToMask(){
static_assert(i < Size,"bit position out of range");
return 1 << i;
}
Run Code Online (Sandbox Code Playgroud) 似乎_BitScanReverse,尽管是一个内在的而不是一个真正的函数,但是在Visual C++中的constexpr函数中无法调用它.我知道我可以用更慢的方式自己实现这个操作,这对于在编译时进行评估的情况很好,但不幸的是它不仅仅是1个时钟周期的单CPU指令(BSR)用于在运行时进行评估的情况.我还没有尝试过GCC/Clang中的__builtin_clz,但它可能会或可能不会出现同样的问题,我希望这些代码可以在主要的编译器中运行,(非GCC,非Clang的缓慢回退) ,非VC编译器).
想法/问题:
有一种简单的方法可以让函数在编译时进行评估时使用一个代码块,这样它可以是constexpr-safe,并且是一个不同的运行时代码块,因此它可以很快?(如果是这样,这也与我的其他一些问题有关.)
或者,有没有办法欺骗编译器能够为constexpr代码评估_BitScanReverse?
问题:
是否有任何计划最终将其添加到C++标准?他们添加了std :: log2和std :: ilogb,但这些都是通过浮点数,而不是只做一个BSR(或者是ARM芯片上的CLZ和减法).
struct root
{
int i = 0;
};
struct base: root{};
struct derive: base{};
constexpr derive d0;
int main()
{
constexpr auto& r = static_cast<root const&>(d0);
constexpr auto& b = static_cast<base const&>(r);
constexpr auto& d = static_cast<derive const&>(r);
static_assert(d0.i == 0, ""); // ok
static_assert(r.i == 0, ""); // ok
static_assert(b.i == 0, ""); // error in gcc
static_assert(d.i == 0, ""); // ok
}
Run Code Online (Sandbox Code Playgroud)
Clang接受上面的代码,但是gcc 7.2.0编译错误如下:
prog.cc:17:5: error: non-constant condition for static assertion
static_assert(b.i == 0, "");
^~~~~~~~~~~~~
prog.cc:17:5: …Run Code Online (Sandbox Code Playgroud) 我想初始化一个std :: map,键是a constexpr.考虑以下C++ 11 MWE:
#include <map>
using std::map;
constexpr unsigned int str2int(const char* str, const int h = 0) {
return !str[h] ? 5381 : (str2int(str, h + 1) * 33) ^ str[h];
}
const map<unsigned int, const char*> values = {
{str2int("foo"), "bar"},
{str2int("hello"), "world"}
};
int main() { return 0; }
Run Code Online (Sandbox Code Playgroud)
当代码编译最近的clang和gcc时,生成的二进制文件将包含密钥类型的字符串:
为什么密钥包含在二进制文件中,即使它们被用作constexpr?有办法解决这个问题吗?
当然,映射初始化将在运行时进行.但是不应该在编译时用constexpr替换二进制文件中的值吗?
注意:这当然是一个简化的例子.我知道有不同的增强结构可能更适合这种用例.我特别感兴趣的是为什么会这样.
[编辑]
无论是否启用优化,都会发生此行为.以下代码使用bar作为字符串表中唯一的用户定义字符串进行编译:
#include <map>
#include <iostream>
#include <string>
using namespace std;
constexpr unsigned int …Run Code Online (Sandbox Code Playgroud) 在cppref中,它给出了常量初始化的语法:
static T & ref = constexpr;
static T object = constexpr;
Run Code Online (Sandbox Code Playgroud)
这是我的两个问题:
一个左值引用怎么可能T &没有const绑定到a constexptr,它是常量且不可修改的?
我试着提供一些例子,但失败了:
static int& ref = 6; //error, need a `const`
constexpr int a = 6; static int& ref = a; //error, need a `const`
Run Code Online (Sandbox Code Playgroud)
对于持续初始化的对象是否必须是const/ static?在标准中它说:
执行恒定初始化如果与静态或线程存储持续时间的变量或临时对象由恒定初始化为实体初始化.
这里标准没有将obj指定为const-qualified/ static-qualified.
使用constexpr-specified函数,foo_constexpr我的代码如下所示:
const auto x = foo_constexpr(y);
static_assert(x==0);
Run Code Online (Sandbox Code Playgroud)
在的声明x更改为的情况下,代码在什么情况下然后可能无法编译constexpr?(毕竟,x必须已经是用于中的常量表达式static_assert。)即:
constexpr auto x = foo_constexpr(y);
static_assert(x==0);
Run Code Online (Sandbox Code Playgroud)