如何与标准元组操作正确转发和使用ConstexPR结构的嵌套元组
我想通过constexpr
struct> struct> struct 存储传递的数据,然后将数据存储在
std :: tuple ,执行各种TMP /编译时间操作。
实施
template <typename... _Ts>
struct myInitializer {
std::tuple<_Ts...> init_data;
constexpr myInitializer(_Ts&&... _Vs)
: init_data{ std::tuple(std::forward<_Ts>(_Vs)...) }
{}
};
存储的数据使用轻量级强类型
结构,通过lvalue和rvalue助手重载生成:
template <typename T, typename... Ts>
struct data_of_t {
using type = T;
using data_t = std::tuple<Ts...>;
data_t data;
constexpr data_of_t(Ts&&... _vs)
: data(std::forward<Ts>(_vs)...)
{}
};
template<typename T, typename... Ts>
constexpr auto data_of(Ts&&... _vs) {
return data_of_t<T, Ts...>(std::forward<Ts>(_vs)...);
};
template<typename T, typename... Ts>
constexpr auto data_of(Ts&... _vs) {
return data_of_t<T, Ts...>(std::forward<Ts>(_vs)...);
};
它是实现的,例如
template <typename T = int>
class test {
public:
static constexpr auto func(int p0=0, int p1=1, int p2=3) noexcept {
return data_of <test<T>>
(data_of<test<T>>(p0, p1));
}
};
int main() {
constexpr // fails to run constexpr // works without
auto init = myInitializer (
test<int>::func()
,test<int>::func(3)
,test<int>::func(4,5)
);
std::apply([&](auto&&... args) {
//std::cout << __PRETTY_FUNCTION__ << std::endl;
auto merged_tuple = std::tuple_cat(std::forward<decltype(args.data)>(args.data)...);
}
, init.init_data);
}
到达点
std :: tuple_cat如果MyInitializer实例为constexpr
,则会失败。
std::apply([&](auto&&... args) {
auto merged_tuple = std::tuple_cat(std::forward<decltype(args.data)>(args.data)...);
它似乎与const
通过constexpr
添加的预选赛有关。
如何修复?
请参阅
I want to store passed data via constexpr
constructor
of a struct
, and store the data in a std::tuple
, to perform various TMP / compile time operations.
Implementation
template <typename... _Ts>
struct myInitializer {
std::tuple<_Ts...> init_data;
constexpr myInitializer(_Ts&&... _Vs)
: init_data{ std::tuple(std::forward<_Ts>(_Vs)...) }
{}
};
Stored data uses a lightweight strong type
struct, generated via lvalue and rvalue helper overload:
template <typename T, typename... Ts>
struct data_of_t {
using type = T;
using data_t = std::tuple<Ts...>;
data_t data;
constexpr data_of_t(Ts&&... _vs)
: data(std::forward<Ts>(_vs)...)
{}
};
template<typename T, typename... Ts>
constexpr auto data_of(Ts&&... _vs) {
return data_of_t<T, Ts...>(std::forward<Ts>(_vs)...);
};
template<typename T, typename... Ts>
constexpr auto data_of(Ts&... _vs) {
return data_of_t<T, Ts...>(std::forward<Ts>(_vs)...);
};
It's implemented like
template <typename T = int>
class test {
public:
static constexpr auto func(int p0=0, int p1=1, int p2=3) noexcept {
return data_of <test<T>>
(data_of<test<T>>(p0, p1));
}
};
int main() {
constexpr // fails to run constexpr // works without
auto init = myInitializer (
test<int>::func()
,test<int>::func(3)
,test<int>::func(4,5)
);
std::apply([&](auto&&... args) {
//std::cout << __PRETTY_FUNCTION__ << std::endl;
auto merged_tuple = std::tuple_cat(std::forward<decltype(args.data)>(args.data)...);
}
, init.init_data);
}
Getting to the point
std::tuple_cat fails if myInitializer instance is constexpr
.
std::apply([&](auto&&... args) {
auto merged_tuple = std::tuple_cat(std::forward<decltype(args.data)>(args.data)...);
It appears to be related to the const
qualifier added via constexpr
.
How can this be fixed?
See full example at https://godbolt.org/z/j5xdT39aE
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这:
不是转发数据的正确方法。
code> exltype(args.data)
将为您提供该数据成员的类型 - 这不是args args
的const -ness或值类别的函数。让我们以一个更简单的示例:在这里我有三个呼叫
f
(呼叫f&lt; c&amp;&gt;
,f&lt; const c&amp c&amp;&gt; 和
f&lt; c&gt;
)。在所有三种情况下,code> nectType(arg.data)
is ... Justint
。这就是c :: data
的类型。但这不是不是需要如何转发它(它不会为c2
编译,因为我们正在尝试将const-抛弃 - 就像您的示例一样 - 并且它会错误地移出c1
)。您想要的是转发
arg
,分别和然后访问数据:现在,
exltype(arg)
实际上从实例化到实例化而有所不同,是一个很好的指标,即我们正在做明智的事情。This:
is not the right way to forward data.
decltype(args.data)
is going to give you the type of that data member - which is not a function of either the const-ness or value category ofargs
. Let's take a simpler example:So here I have three calls to
f
(which callf<C&>
,f<const C&>
, andf<C>
, respectively). In all three cases,decltype(arg.data)
is... justint
. That's what the type ofC::data
is. But that's not how it needs to be forwarded (it won't compile forc2
because we're trying to cast away const-ness -- as in your example -- and it'll erroneously move out ofc1
).What you want is to forward
arg
, separately, and then access data:Now,
decltype(arg)
actually varies from instantiation to instantiation, which is a good indicator that we're doing something sensible.除了Barry表示的转发问题外,您不能在
Init
上拥有ConstexPR的原因不同。这是因为您在data_of_t
中包含对临时性的引用。您会看到,您包含了从转发参考获得的从过载分辨率获得的类型:
在这种情况下,
ts ...
可能是int,float const&amp;,double&amp; 。您将这些参考类型发送,然后将它们包含在
std :: tuple
indata_of_t
中。这些临时性是
test
函数的本地变量:这里的问题是
p0
,p1
,p2
都是局部变数。您将它们发送到包含对它们的引用的test_of_t
中,然后返回包含所有引用对本地变量的对象。这也许是MSVC崩溃的原因。需要编译器为ConstexPR上下文中的任何未定义行为提供诊断。此崩溃是100%的编译器错误,您应该报告它。那么您如何解决呢?
简而言之,不要通过更改
data_of
:这将衰减类型,从而删除引用并衰减对指针的任何引用。
然后,您必须更改构造函数。您可以在其中调用
std :: forward
,但是如果您在模板参数中衰减,则不会发生转发。这将添加适当的转发并正确约束,以便它始终按预期的
data_of
进行操作。仅执行这些更改将从代码中删除UB,但也将其更改。类型
data_of_t
将始终包含值,并且不会包含引用。如果要发送引用,您将需要std :: ref
之类的东西,就像std :: bind
必须使用以推迟参数。您仍然需要使用
std :: forward&lt; exptype(arg)&gt;(arg).data
正确转发如@barry所述In addition of the forwarding problem denoted by Barry, there's a different reason why you cannot have constexpr on
init
. This is because you contain a reference to a temporary insidedata_of_t
.You see, you are containing a type obtained from overload resolution from a forwarding reference:
The
Ts...
in this case could be something likeint, float const&, double&
. You send those reference type and then you contain them inside of thestd::tuple
indata_of_t
.Those temporaries are local variables from the
test
function:The problem here is that
p0
,p1
,p2
are all local variable. You send them intest_of_t
which will contain references to them, and you return the object containing all those reference to the local variable. This is maybe the cause of the MSVC crash. Compiler are required to provide diagnostic for any undefined behaviour in constexpr context. This crash is 100% a compiler bug and you should report it.So how do you fix that?
Simply don't contain references by changing
data_of
:This will decay the type thus removing the references and decay any reference to C array to pointers.
Then, you have to change your constructor. You call
std::forward
in there but it's no forwarding occurring if you decay in the template arguments.This will add proper forwarding and also constrain it properly so it always do as
data_of
intended.Just doing those change will remove UB from the code, but also change it a bit. The type
data_of_t
will always contain values, and won't contain references. If you want to send a reference, you will need something likestd::ref
, just likestd::bind
have to use to defer parameters.You will still need to use
std::forward<decltype(arg)>(arg).data
for proper forwarding as @Barry stated