据我了解,constexpr与模板元编程不同,图灵不是完整的,所以我相信这些不一样.那么问题是constexpr模板元编程在多大程度上已经过时了?
考虑一个只在运行时包装值的类:
template <typename Type>
class NonConstValue
{
public:
NonConstValue(const Type& val) : _value(val) {;}
Type get() const {return _value;}
void set(const Type& val) const {_value = val;}
protected:
Type _value;
};
Run Code Online (Sandbox Code Playgroud)
和constexpr版本:
template <typename Type>
class ConstValue
{
public:
constexpr ConstValue(const Type& val) : _value(val) {;}
constexpr Type get() const {return _value;}
protected:
const Type _value;
};
Run Code Online (Sandbox Code Playgroud)
问题1:您能否确认constexpr版本是否以正确的方式设计?
问题2:如何将两个类混合成一个名为Value可以constexpr构造或运行时构造的类,其值可以get()在运行时或编译时?
编辑:问题3:如果get()在.cpp文件中定义,如果我想get()内联,如果它不是一个constexpr正确的函数声明?是吗
constexpr inline Type get();
Run Code Online (Sandbox Code Playgroud)
要么
inline …Run Code Online (Sandbox Code Playgroud) N2976建议添加constexpr标准库中的某些位置.它指出iostreams不适用于constexprEXCEPT结束迭代器.所以istream_iterator,istreambuf_iterator并给予constexpr默认构造函数,就是这样.例如,您可以在libstdc ++实现中看到constexpr只在整个文件中出现一次.引发这一变化的LWG是#1129.它说:
istream_iterator并istreambuf_iterator应支持文字哨兵价值观.默认构造函数经常用于终止范围istreambuf_iterator,并且istream_iterator在迭代值类型时很容易成为文字值 .[其余省略]
这对我来说并没有多大意义.有人能告诉我他们的意思吗?
N3308是另一篇提到但没有解释问题的论文:
某些
istream_iterator<T>构造函数必须是constexprifT是文字类型.目的是允许存储一种T内联类型的现有实现技术继续工作.[libstdc ++这样做,_Tp _M_value]但是,它实际上排除了这种技术:T不需要标记默认和复制构造函数constexpr,如果不是,则istream_iterator<T>构造函数不能被实例化constexpr.
上面解释了普通的复制构造函数和析构函数,但不是默认构造函数标记为constexpr的原因.
此外,在在线GCC 5.2.0上进行测试,我复制了libstdc ++的实现.唯一的变化是我删除了constexpr istream_iterator().在这两种情况下,组件都是相同的.
我从N4140的§5.19/ 2得到了这个例子:
constexpr int incr(int &n) {
return ++n;
}
Run Code Online (Sandbox Code Playgroud)
据我所知,这不是一个constexpr功能.但是代码片段用clang和g ++编译.查看实例.我在这里错过了什么?
C++ 17更新:
static constexpr变量是隐式的,inline因此不需要外部定义.
原始问题:
假设我有一个常量列表,例如
struct Cls {
static constexpr int N = 32;
static constexpr int M = 64;
};
Run Code Online (Sandbox Code Playgroud)
这当然表明我为这些添加定义以避免可能发生的ODR使用问题,因此我需要:
constexpr int Cls::N;
constexpr int Cls::M;
Run Code Online (Sandbox Code Playgroud)
为什么要我喜欢这个了
struct Cls {
enum : int {
N = 32,
M = 64
};
};
Run Code Online (Sandbox Code Playgroud)
从而节省了我的ODR使用难题N,M并且更真实地只是常量而不是它们自己的对象(如果这只是标题,那就更大了)并且更短.enum : long long如果需要,我可以明确指定类型或其他任何东西.第一个有什么优势?
std::addressofC++ 17 的规范已经改变:现在允许它是一个常量表达式.但是,cppreference说:
表达式
std::addressof(E)是一个常量子表达式,如果E是左值常量子表达式.
std::addressof(E)恒定表达式的例子?std::addressof(E)不是常量表达式的例子?C++17§10.1.5/ 1指出:
的
constexpr说明符将只应用于一个变量或变量模板的定义或功能或功能模板的声明.使用说明constexpr符声明的函数或静态数据成员 隐式地是内联函数或变量(10.1.6).如果函数或函数模板的任何声明都有一个constexpr说明符,那么它的所有声明都应该包含说明constexpr符.
因为类似的段落已在标准中存在C++ 11(§7.1.5/ 1),这是在引述由理查德·史密斯评论,其中他争辩说,C++标准并没有要求constexpr说明符之间匹配声明和变量的定义.在上段的最后一句明确要求constexpr符横跨匹配功能和函数模板的声明,但并没有提及变量声明.
§10.1.5/ 9指出:
constexpr对象声明中使用的说明符将对象声明为const.这样的对象应具有文字类型并应初始化.在任何constexpr变量声明中,初始化的完整表达式应为常量表达式(8.20).
当然,如果我们有一个单独的声明和定义,它们都需要匹配const,无论constexpr指定符是否需要匹配.
§12.2.3.2/ 2-3说:
2在其类定义中声明非内联静态数据成员不是定义,并且可能是除cv 之外的不完整类型
void.未在类定义中内联定义的静态数据成员的定义应出现在包含成员类定义的命名空间范围内.在命名空间作用域的定义中,静态数据成员的名称应使用::运算符通过其类名限定.静态数据成员定义中的初始化表达式在其类的范围内(6.3.7).3如果非易失性非内联
const静态数据成员具有整数或枚举类型...如果使用说明constexpr符声明成员,则可以在没有初始化程序的命名空间范围内重新声明该成员 (此用法已弃用;请参阅D.1 ).其他静态数据成员的声明不应指定大括号或等于初始化器.
§D.1/ 1内容如下:
为了与先前的C++国际标准兼容,
constexpr可以在类外部冗余地重新声明静态数据成员而不使用初始化程序.不推荐使用此用法.
从中我们可以收集到,如果使用说明符声明成员constexpr,则命名空间范围定义是多余的, …
所以我有以下简单的片段:
template <typename T, size_t size>
struct SquareMatrix {
public:
T data[size * size];
constexpr T & operator()(const size_t row, const size_t col) noexcept {
return data[row * size + col];
}
};
constexpr auto generate() {
auto result = SquareMatrix<int, 2>{};
result(0, 0) = 1;
result(1, 0) = 3;
result(0, 1) = 2;
result(1, 1) = 4;
return result;
}
Run Code Online (Sandbox Code Playgroud)
data所SquareMatrix<int, 2>产生的数组的预期内容generate()是1, 2, 3, 4.然而...
constexpr auto test = generate();
int main() …Run Code Online (Sandbox Code Playgroud) 在 C++11 constexpr 函数中,assert()不可能使用第二个语句,例如 an 。Astatic_assert()很好,但如果该函数被称为“正常”函数,则它不起作用。逗号运算符可以来帮助wrto。的assert(),而是丑陋和一些工具吐出它的警告。
考虑这样的“getter”,它在断言旁边是完全可构造的。但是我想为运行时和编译时保留某种断言,但不能仅根据“constexpr”上下文进行重载。
template<int Size>
struct Array {
int m_vals[Size];
constexpr const int& getElement( int idx ) const
{
ASSERT( idx < Size ); // a no-go for constexpr funcs in c++11
// not possible, even in constexpr calls as being pointed out, but what I would like:
static_assert( idx < Size, "out-of-bounds" );
return m_vals[idx];
}
};
Run Code Online (Sandbox Code Playgroud)
附带条件:C++11,无堆,无异常,无编译器细节。
注意正如评论者指出的那样(谢谢!),static_assert论点是不可能的(但会很好)。在这种情况下,编译器在越界访问时给了我一个不同的错误。
我有什么保证可能包含 constexpr 函数调用的核心常量表达式(如在 [expr.const].2 中)将在编译时实际评估,这取决于哪些条件?
constexpr通过将计算移至翻译阶段(编译时),引入隐式承诺提高运行时性能。这两点似乎相互矛盾。
在什么情况下可以依靠编译器在编译时解析核心常量表达式(可能包含任意复杂的计算)而不是将其推迟到运行时?
至少在-O0gcc下似乎实际上发出代码并调用 constexpr 函数。在-O1最多不。
难道我们不得不求助于挂羊头卖狗肉,如这个,那个力量通过模板系统constexpr:
template <auto V>
struct compile_time_h { static constexpr auto value = V; };
template <auto V>
inline constexpr auto compile_time = compile_time_h<V>::value;
constexpr int f(int x) { return x; }
int main() {
for (int x = 0; x < compile_time<f(42)>; ++x) {}
}
Run Code Online (Sandbox Code Playgroud)