如何解决当“失败是由在其生命周期之外读取变量引起的”时如何使 Constexpr 函数
我有一个 struct
专门化的 enum Id
。我有一个 idOf
函数,它接受一个 Type<Id>
并返回模板参数。
我无法真正修改所有类型以包含额外的成员,并且我认为这必须非常简单地解决为 constexpr
。
当 Type 对象的初始化在同一个代码模块之外时,我遇到了一个问题,因为使用我的 constexpr
函数(在代码示例中作为注释嵌入),我遇到了编译错误。在示例中,我使用 getA()
方法返回引用来导致这种情况,但是 idOf()
实际上并不依赖于结构的实际内容,而只依赖于它的类型。
实时代码示例:https://godbolt.org/z/8f649T64c
#include <cstdint>
enum Id { A,B,C };
template<Id cId>
struct Type;
template<>
struct Type<A> { int t; };
template<Id cId>
constexpr Id idOf( const Type<cId>& = {} )
{ return cId; }
Type<A>& getA();
int main( int argc,char**)
{
Type<A>& a = getA();
/// ERROR: error C2131: expression did not evaluate to a constant
/// message: failure was caused by a read of a variable outside its lifetime
/// message : see usage of 'data'
constexpr Id id = idOf( a );
return (int)id;
}
非常感谢帮助:D
编辑:最简单的解决方法是丑陋的,但会断开与范围之外形成的对象值的连接。这感觉像是一个完整的“黑客”,但我把它放在这里以供参考:
constexpr Id id = idOf( std::decay_t<decltype(a)>() )
这本质上是解析为 `id = idOf( Type() ) ,这与 constexpr 一样好。
解决方法
问题是你的参考。不允许在常量表达式中使用引用,除非:
引用变量或引用类型数据成员的 id 表达式,除非该引用可用于常量表达式(见下文)或其生命周期在此表达式的计算内开始
如果您将其从引用更改为值,没问题。
template<Id cId>
constexpr std::integral_constant<Id,cId> idOf( const Type<cId>& = {} )
{ return {}; }
这里我对类型中的返回值进行编码。
constexpr Id id = decltype(idOf( a ))::value;
我在这里提取它。
另一种使用标签的方法:
template<class T>
struct tag_t {using type=T;};
template<class T>
constexpr tag_t<T> tag{};
template<class T>
constexpr tag_t<std::decay_t<T>> dtag{};
然后我们添加:
template<Id cId>
constexpr std::integral_constant<Id,cId> idOf( tag_t<Type<cId>> = {} )
{ return {}; }
我们可以这样做:
constexpr Id id2 = idOf(dtag<decltype( a )>);
题外话:
tag_t
很有用,因为它允许您将类型作为值传递。所以我可以在没有模板 lambda 支持的情况下将类型传递给 lambda,或者存储类型的变体(不是该类型的值,而是类型)。
所以它不是一次性的,而是在其他地方有用的东西。例如:
template<class...Ts>
using types = std::variant<tag_t<Ts>...>;
template<class...Ts>
constexpr types<Ts...> get_type( std::variant<Ts...> const& v ) {
constexpr types<Ts...> table[] = {
tag<Ts>...
};
if (v.valueless_by_exception())
throw std::bad_variant_access{};
return table[v.index()];
}
这里我只是对 variant
上的类型进行了枚举,可以使用 std::visit
将其转换回类型本身。
我得到的最接近的解决方案是使用模板对象来确定 Id
而不是使用 constexpr
表达式:
template <typename cId>
struct IdOf;
template <Id cId >
struct IdOf< Type<cId> > : std::integral_constant<Id,cId> {};
template<typename T>
constexpr Id idOf = IdOf< std::decay_t<T> >::value;
这会将调用方站点更改为:
constexpr Id id = idOf<decltype(a)>;
这还不错,但对我来说仍然感觉像是一种“解决方法”,除非此问题确定了 constexpr
在这里实时编译代码:https://godbolt.org/z/s5bTTdqW9
编辑:添加了 idOf<>
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。