"failure was caused by a read of a variable outside its lifetime" 时如何使 Constexpr 函数
How to make Constexpr function when "failure was caused by a read of a variable outside its lifetime"
我有一个 enum Id
专门化的 struct
。我有一个 idOf
函数,它采用 Type<Id>
和 returns 模板参数。
我真的无法修改所有类型以包含一个额外的成员,并认为这必须非常简单才能解决 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
编辑:最简单的解决方法是丑陋的,但会断开与范围外的对象值的连接。这感觉像是一个完整的 'hack' 但我把它放在这里以供参考:
constexpr Id id = idOf( std::decay_t<decltype(a)>() )
这本质上是解析为 `id = idOf( Type() ) 这与 constexpr.
一样好
问题是你的参考。不允许在常量表达式中使用引用,除非:
an id-expression referring to a variable or a data member of reference type, unless the reference is usable in constant expressions (see below) or its lifetime began within the evaluation of this expression
如果您将它从引用更改为值,没问题。
template<Id cId>
constexpr std::integral_constant<Id, cId> idOf( const Type<cId>& = {} )
{ return {}; }
这里我将 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)>;
它还不错,但对我来说仍然感觉像 'workaround',除非这个问题确定了 constexpr
的实际限制
在此处实时编译代码:https://godbolt.org/z/s5bTTdqW9
编辑:添加了 idOf<>
我有一个 enum Id
专门化的 struct
。我有一个 idOf
函数,它采用 Type<Id>
和 returns 模板参数。
我真的无法修改所有类型以包含一个额外的成员,并认为这必须非常简单才能解决 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
编辑:最简单的解决方法是丑陋的,但会断开与范围外的对象值的连接。这感觉像是一个完整的 'hack' 但我把它放在这里以供参考:
constexpr Id id = idOf( std::decay_t<decltype(a)>() )
这本质上是解析为 `id = idOf( Type() ) 这与 constexpr.
问题是你的参考。不允许在常量表达式中使用引用,除非:
an id-expression referring to a variable or a data member of reference type, unless the reference is usable in constant expressions (see below) or its lifetime began within the evaluation of this expression
如果您将它从引用更改为值,没问题。
template<Id cId>
constexpr std::integral_constant<Id, cId> idOf( const Type<cId>& = {} )
{ return {}; }
这里我将 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)>;
它还不错,但对我来说仍然感觉像 'workaround',除非这个问题确定了 constexpr
在此处实时编译代码:https://godbolt.org/z/s5bTTdqW9
编辑:添加了 idOf<>