【发布时间】:2015-09-01 10:35:39
【问题描述】:
这个
int main()
{
std::cout << range(1, 11).reverse().sort().sum() << std::endl;
}
是main 中的所有内容,正如代码所说,它创建一个从 1 到 10 的列表,将其反转,通过排序取消反转,并计算总和。因此输出应该是 55。
代码是一个实验,(ab)使用 C++14 中 constexpr 的宽松要求。我尽力创建了一个编译时列表类,但真的做得还不够。该类不完整,但仍然可以模仿很多函数式编程。
据我了解,constexpr 的要求是让编译器在编译时评估事物。所以我认为编译器可以简单地将所有内容替换为我的代码的常量 55,但事实并非如此。该代码确实具有在编译时获得结果所需的一切。我错过了什么?
在 cmets 中,我尝试使用 static_assert 中的结果来检查问题。 clang 和 gcc 都给我报错,但是我也没有看懂,而且后者好像坏了……
叮当声
a.cpp:142:17: error: static_assert expression is not an integral constant expression
static_assert(range(1, 11).reverse().sort().sum() == 55, "");
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
a.cpp:60:23: note: assignment to object outside its lifetime is not allowed in a constant
expression
l.array[l.length++] = t;
^
a.cpp:134:9: note: in call to '&l->add(1)'
l = l.add(a);
^
a.cpp:142:17: note: in call to 'range(1, 11, 1)'
static_assert(range(1, 11).reverse().sort().sum() == 55, "");
^
1 error generated.
gcc
main.cpp: In function 'int main()':
main.cpp:142:3: error: non-constant condition for static assertion
static_assert(range(1, 11).reverse().sort().sum() == 55, "");
^
main.cpp:142:38: error: 'constexpr List<T> List<T>::reverse() const [with T = int]' called in a constant expression
static_assert(range(1, 11).reverse().sort().sum() == 55, "");
^
main.cpp:74:19: note: 'constexpr List<T> List<T>::reverse() const [with T = int]' is not usable as a constexpr function because:
constexpr List<T> List<T>::reverse() const
^
main.cpp:74:19: sorry, unimplemented: unexpected AST of kind result_decl
main.cpp:74: confused by earlier errors, bailing out
完整代码
#include <cstdint>
#include <iostream>
#include <limits>
#include <algorithm>
#include <initializer_list>
template<typename T>
class List
{
template<typename T2>
friend std::ostream &operator<<(std::ostream &, const List<T2> &);
public:
constexpr List();
constexpr List(std::initializer_list<T>);
constexpr T head() const;
constexpr List<T> tail() const;
constexpr List<T> add(T) const;
constexpr List<T> merge(List<T>) const;
constexpr List<T> reverse() const;
template<typename Filter>
constexpr List<T> filter(Filter) const;
constexpr List<T> sort() const;
constexpr T sum() const;
private:
int length;
T array[0x100];
};
template<typename T>
constexpr List<T>::List()
: length(0)
{
}
template<typename T>
constexpr List<T>::List(std::initializer_list<T> l)
: length {static_cast<int>(l.size())}
{
std::copy(l.begin(), l.end(), array);
}
template<typename T>
constexpr T List<T>::head() const
{
return array[0];
}
template<typename T>
constexpr List<T> List<T>::tail() const
{
List<T> l;
l.length = length - 1;
std::copy_n(array + 1, l.length, l.array);
return l;
}
template<typename T>
constexpr List<T> List<T>::add(T t) const
{
List<T> l {*this};
l.array[l.length++] = t;
return l;
}
template<typename T>
constexpr List<T> List<T>::merge(List<T> l) const
{
std::copy_backward(l.array, l.array + l.length, l.array + l.length + length);
std::copy_n(array, length, l.array);
l.length += length;
return l;
}
template<typename T>
constexpr List<T> List<T>::reverse() const
{
List<T> l;
l.length = length;
std::reverse_copy(array, array + length, l.array);
return l;
}
template<typename T>
template<typename Filter>
constexpr List<T> List<T>::filter(Filter f) const
{
List<T> l;
for (int i {0}; i < length; ++i)
{
if (f(array[i]))
{
l = l.add(array[i]);
}
}
return l;
}
template<typename T>
constexpr List<T> List<T>::sort() const
{
if (length == 0)
{
return *this;
}
return tail().filter([&](T t) {return t < head();}).sort().add(head())
.merge(tail().filter([&](T t) {return t >= head();}).sort());
}
template<typename T>
constexpr T List<T>::sum() const
{
if (length == 0)
{
return T {};
}
return head() + tail().sum();
}
template<typename T>
std::ostream &operator<<(std::ostream &os, const List<T> &l)
{
os << '{';
for (int i {0}; i < l.length - 1; ++i)
{
os << l.array[i] << ", ";
}
return os << l.array[l.length - 1] << '}';
}
inline constexpr List<int> range(int a, int b, int c = 1)
{
List<int> l;
while (a < b)
{
l = l.add(a);
a += c;
}
return l;
}
int main()
{
static_assert(range(1, 11).reverse().sort().sum(), "");
std::cout << range(1, 11).reverse().sort().sum() << std::endl;
}
【问题讨论】:
-
最好检查一下:
static_assert(range(1, 11).reverse().sort().sum() == 55, "");,编译器会告诉你,哪一部分不能用在常量表达式中。 -
@ForEveR 我现在收到一条错误消息:
note: assignment to object outside its lifetime is not allowed in a constant expression l.array[l.length++] = t;,你能帮我解释一下这是什么意思吗? -
c++ 只说表达式必须能够在编译时计算。这并不意味着它是在编译期间完成的。这正是我为此使用 MTP 的原因。您是否尝试使结果成为编译时间常数的值,例如对于模板 int 参数常量?
-
gcc 给了我一个有趣的错误信息
main.cpp:74:19: note: 'constexpr List<T> List<T>::reverse() const [with T = int]' is not usable as a constexpr function because: sorry, unimplemented: unexpected AST of kind result_decl confused by earlier errors, bailing out...