Lon*_*ong 10 c++ boost boost-hana
我在 Boost.hana 中使用 c++17 来编写一些元编程程序。困扰我的一个问题是在像 static_assert 这样的 constexpr 上下文中可以使用什么样的表达式。下面是一个例子:
#include <boost/hana.hpp>
using namespace boost::hana::literals;
template <typename T>
class X {
public:
T data;
constexpr explicit X(T x) : data(x) {}
constexpr T getData() {
return data;
}
};
int main() {
{ // test1
auto x1 = X(1_c);
static_assert(x1.data == 1_c);
static_assert(x1.getData() == 1_c);
}
{ //test2.1
auto x2 = X(boost::hana::make_tuple(1_c, 2_c));
static_assert(x2.data[0_c] == 1_c);
// static_assert(x2.getData()[0_c] == 1_c); // read of non-constexpr variable 'x2' is not allowed in a constant expression
}
{ //test2.2
auto x2 = X(boost::hana::make_tuple(1_c, 2_c));
auto data = x2.getData();
static_assert(data[0_c] == 1_c);
}
}
Run Code Online (Sandbox Code Playgroud)
首先,我使用字段data和访问器getData()编写了一个类X。在main()的test1部分,x1.data和x1.getData() 的行为与我预期的一样。但是在test2部分,将参数更改为 boost::hana 的元组,仍然表现良好,但编译失败,出现错误“读取非 constexpr 变量 'x2' 不允许在常量表达式中”。如果我拆什么weired是进入并再次罚款编译。我希望他们的行为相同。那么谁能帮助解释为什么在这个例子中不能在 static_assert 中使用?static_assert(x2.data[0_c] == 1_c)
static_assert(x2.getData()[0_c] == 1_c)
x2.getData()[0_c]
auto data = x2.getData();
static_assert(data[0_c] == 1_c);
x2.getData()[0_c]
重现:clang++8.0 -I/path/to/hana-1.5.0/include -std=c++17 Test.cpp
问题是boost::hana::tuple
没有复制构造函数。
它有一个构造函数是看起来像一个拷贝构造函数:
template <typename ...dummy, typename = typename std::enable_if<
detail::fast_and<BOOST_HANA_TT_IS_CONSTRUCTIBLE(Xn, Xn const&, dummy...)...>::value
>::type>
constexpr tuple(tuple const& other)
: tuple(detail::from_index_sequence_t{},
std::make_index_sequence<sizeof...(Xn)>{},
other.storage_)
{ }
Run Code Online (Sandbox Code Playgroud)
但由于这是一个模板,它不是一个复制构造函数。
由于boost::hana::tuple
没有复制构造函数,一个被隐式声明并定义为默认值(它没有被抑制,因为boost::hana::tuple
没有任何复制或移动构造函数或赋值运算符,因为,你猜对了,它们不能是模板)。
在这里,我们看到了实现分歧,在以下程序的行为中得到了证明:
struct A {
struct B {} b;
constexpr A() {};
// constexpr A(A const& a) : b{a.b} {} // #1
};
int main() {
auto a = A{};
constexpr int i = (A{a}, 0);
}
Run Code Online (Sandbox Code Playgroud)
gcc 接受,而 Clang 和 MSVC 拒绝,但如果行#1
未注释则接受。也就是说,编译器不同意非(直接)空类的隐式定义的复制构造函数是否允许在常量评估上下文中使用。
根据隐式定义的复制构造函数的定义,#1 与constexpr A(A const&) = default;
gcc没有任何不同,因此 gcc 是正确的。还要注意,如果我们给 B 一个用户定义的 constexpr 复制构造函数 Clang 和 MSVC 再次接受,那么问题似乎是这些编译器无法跟踪递归空的隐式可复制类的 constexpr 复制构造性。为MSVC和Clang提交了错误(针对 Clang 11修复)。
注意使用的operator[]
是红鲱鱼;问题是编译器是否允许呼叫getData()
(其中复制构造T
一个恒定的评估范围内),如static_assert
。
显然,理想的解决方案是 Boost.Hana 进行更正boost::hana::tuple
,使其具有实际的复制/移动构造函数和复制/移动赋值运算符。(这将解决您的用例,因为代码将调用用户提供的复制构造函数,这在常量评估上下文中是允许的。)作为一种解决方法,您可以考虑通过 hackinggetData()
来检测无状态的情况T
:
constexpr T getData() {
if (data == T{})
return T{};
else
return data;
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
626 次 |
最近记录: |