静态 constexpr 数据成员已初始化为 off-class,以及可能的解决方法?
Static constexpr data member initialized off-class, and a possible workaround?
从 I know that it's not possible in C++17 to declare a static constexpr data member without its immediate initialization (although yet elsewhere 他们使用这样的例子)。
// --- in header ---
struct Data{
LPCTSTR str;
int i;
};
class C{
public:
static constexpr Data MyData; // VS complains: this requires an in-class initializer
};
// --- in implementation file ---
constexpr Data C::MyData={
_T("Hi"), 12345
};
我的代码暂时还必须在较旧的 Visual Studio 中编译,其中缺少 constexpr 概念很容易被#defined as
#if _MSC_VER<=1600 // pre-C++11 compiler?
#define constexpr const
#endif
突然之间,代码库将与从 VS6 到 VS2019 的几乎任何东西兼容。如果通过 Visual Studio.
中的某些隐藏命令行开关允许 off-class 初始化,请纠正我
所以我想出了一个我不确定其有效性的解决方法。与数据成员不同,静态 constexpr 方法不必具有 in-class 主体。所以我这样做:
// --- in header ---
struct Data{
LPCTSTR str;
int i;
};
class C{
public:
static constexpr Data GetMyData();
};
// --- in implementation file ---
constexpr Data C::GetMyData(){
constexpr Data D={
_T("Hi"), 12345
};
return D;
}
然后我可以从 constexpr 中受益,但仍然可以在旧的 Visual Studios:
下编译(借助上面的 #define)
constexpr LPCTSTR Greeting=C::GetMyData().str;
并且还出人意料地在 运行 时获取返回对象的地址:
const Data *p=&C::GetMyData();
const LPCTSTR greeting=p->str;
这在我的应用程序中非常棒并且值得期待!但是让我担心我收到的什么地址?它是编译到生成的可执行文件中的对象的地址(并且在 运行 时驻留在受保护的内存中)还是在 运行 时真正创建的全局变量的地址?我当然希望在较新的 Visual Studios 下编译时更早发生,而我不关心 C++11 之前版本的情况。
提前致谢。
重点是constexpr
并不是说“只在编译时存在”,而是“也可以在编译时使用。所以只要由于您不以 要求 值在编译时可访问的方式访问 constexpr
变量,因此可以将其视为 const
.
事实上,对于您发布的代码,您看到 constexpr
好处的唯一地方是在实现文件中。在其他任何地方,它别无选择,只能“降级”为常规 const
,因为在编译期间解析它的值是不可能的。
我认为你的大部分问题都可以通过将代码解构为等效示例来解决:
// MyClass.h
class MyClass {
// static constexpr Data MyData;
static const Data MyData;
};
// MyClass.cpp
namespace {
constexpr Data MyDataValue = ...;
}
const Data MyData::MyData = MyDataValue;
// If you use MyData inside of the implementation file, it gets the conxtepr value.
void MyClass:foo() {
// std::array<float, MyClass::Data.some_member> some_array;
std::array<float, MyDataValue.some_member> some_array; // no problem
}
// Some_other_file.cpp
int foo() {
// That's fine, MyClass::Data will be resolved either at runtime or during lto.
return MyClass::Data.some_member;
}
int bar() {
std::array<float, MyClass::Data.some_member> some_array; // ERROR! Can't be resolved at compile time
}
那么能够在不立即初始化的情况下声明静态 constexpr
数据成员有什么意义呢?
只要你在实际使用之前在header中定义它,那么一切都很好,有时可以派上用场。但是,在您的情况下,由于您想在实现文件中定义变量,因此您创建了这个两层系统,其中成员在实现文件中为 constexpr
,在其他任何地方为 const
。
这就是说:如果所有延迟的 constexpr
定义都在实现文件中,就像在您发布的代码中一样,那么只需将它们设为 const
,并在本地使用 constexpr
实现文件中的匿名命名空间(如果需要的话)。根本不需要宏。
从
// --- in header ---
struct Data{
LPCTSTR str;
int i;
};
class C{
public:
static constexpr Data MyData; // VS complains: this requires an in-class initializer
};
// --- in implementation file ---
constexpr Data C::MyData={
_T("Hi"), 12345
};
我的代码暂时还必须在较旧的 Visual Studio 中编译,其中缺少 constexpr 概念很容易被#defined as
#if _MSC_VER<=1600 // pre-C++11 compiler?
#define constexpr const
#endif
突然之间,代码库将与从 VS6 到 VS2019 的几乎任何东西兼容。如果通过 Visual Studio.
中的某些隐藏命令行开关允许 off-class 初始化,请纠正我所以我想出了一个我不确定其有效性的解决方法。与数据成员不同,静态 constexpr 方法不必具有 in-class 主体。所以我这样做:
// --- in header ---
struct Data{
LPCTSTR str;
int i;
};
class C{
public:
static constexpr Data GetMyData();
};
// --- in implementation file ---
constexpr Data C::GetMyData(){
constexpr Data D={
_T("Hi"), 12345
};
return D;
}
然后我可以从 constexpr 中受益,但仍然可以在旧的 Visual Studios:
下编译(借助上面的 #define)constexpr LPCTSTR Greeting=C::GetMyData().str;
并且还出人意料地在 运行 时获取返回对象的地址:
const Data *p=&C::GetMyData();
const LPCTSTR greeting=p->str;
这在我的应用程序中非常棒并且值得期待!但是让我担心我收到的什么地址?它是编译到生成的可执行文件中的对象的地址(并且在 运行 时驻留在受保护的内存中)还是在 运行 时真正创建的全局变量的地址?我当然希望在较新的 Visual Studios 下编译时更早发生,而我不关心 C++11 之前版本的情况。
提前致谢。
重点是constexpr
并不是说“只在编译时存在”,而是“也可以在编译时使用。所以只要由于您不以 要求 值在编译时可访问的方式访问 constexpr
变量,因此可以将其视为 const
.
事实上,对于您发布的代码,您看到 constexpr
好处的唯一地方是在实现文件中。在其他任何地方,它别无选择,只能“降级”为常规 const
,因为在编译期间解析它的值是不可能的。
我认为你的大部分问题都可以通过将代码解构为等效示例来解决:
// MyClass.h
class MyClass {
// static constexpr Data MyData;
static const Data MyData;
};
// MyClass.cpp
namespace {
constexpr Data MyDataValue = ...;
}
const Data MyData::MyData = MyDataValue;
// If you use MyData inside of the implementation file, it gets the conxtepr value.
void MyClass:foo() {
// std::array<float, MyClass::Data.some_member> some_array;
std::array<float, MyDataValue.some_member> some_array; // no problem
}
// Some_other_file.cpp
int foo() {
// That's fine, MyClass::Data will be resolved either at runtime or during lto.
return MyClass::Data.some_member;
}
int bar() {
std::array<float, MyClass::Data.some_member> some_array; // ERROR! Can't be resolved at compile time
}
那么能够在不立即初始化的情况下声明静态 constexpr
数据成员有什么意义呢?
只要你在实际使用之前在header中定义它,那么一切都很好,有时可以派上用场。但是,在您的情况下,由于您想在实现文件中定义变量,因此您创建了这个两层系统,其中成员在实现文件中为 constexpr
,在其他任何地方为 const
。
这就是说:如果所有延迟的 constexpr
定义都在实现文件中,就像在您发布的代码中一样,那么只需将它们设为 const
,并在本地使用 constexpr
实现文件中的匿名命名空间(如果需要的话)。根本不需要宏。