C++ 指针可以指向字符串文字的静态成员数组吗?
Can a C++ pointer point to a static member array of string literals?
2017 年 Visual Studio 一切正常,但我在 GCC (6.5.0) 中遇到链接器错误。
下面是一些可以解决我的问题的示例代码:
#include <iostream>
struct Foo{
static constexpr const char* s[] = {"one","two","three"};
};
int main(){
std::cout << Foo::s[0] << std::endl; //this works in both compilers
const char* const* str_ptr = nullptr;
str_ptr = Foo::s; //LINKER ERROR in GCC; works in VS
std::cout << str_ptr[1] << std::endl; //works in VS
return 0;
}
在 GCC 中,我得到 undefined reference to 'Foo::s'
。我需要 Foo::s
的初始化保留在结构的声明中,这就是我使用 constexpr
的原因。有没有办法动态引用 Foo::s
,即使用指针?
更多背景信息
我现在将解释为什么我想这样做。我正在开发嵌入式软件来控制设备的配置。软件加载包含参数名称及其值的配置文件。要配置的参数集在编译时确定,但需要模块化,以便随着设备的继续开发轻松添加新参数并扩展它们。换句话说,它们的定义需要在代码库中的一个地方。
我的实际代码库有数千行,可以在 Visual Studio 中运行,但这里有一个简化的玩具示例:
#include <iostream>
#include <string>
#include <vector>
//A struct for an arbitrary parameter
struct Parameter {
std::string paramName;
int max_value;
int value;
const char* const* str_ptr = nullptr;
};
//Structure of parameters - MUST BE DEFINED IN ONE PLACE
struct Param_FavoriteIceCream {
static constexpr const char* n = "FavoriteIceCream";
enum { vanilla, chocolate, strawberry, NUM_MAX };
static constexpr const char* s[] = { "vanilla","chocolate","strawberry" };
};
struct Param_FavoriteFruit {
static constexpr const char* n = "FavoriteFruit";
enum { apple, banana, grape, mango, peach, NUM_MAX };
static constexpr const char* s[] = { "apple","banana","grape","mango","peach" };
};
int main() {
//Set of parameters - determined at compile-time
std::vector<Parameter> params;
params.resize(2);
//Configure these parameters objects - determined at compile-time
params[0].paramName = Param_FavoriteIceCream::n;
params[0].max_value = Param_FavoriteIceCream::NUM_MAX;
params[0].str_ptr = Param_FavoriteIceCream::s; //!!!! LINKER ERROR IN GCC !!!!!!
params[1].paramName = Param_FavoriteFruit::n;
params[1].max_value = Param_FavoriteFruit::NUM_MAX;
params[1].str_ptr = Param_FavoriteFruit::s; //!!!! LINKER ERROR IN GCC !!!!!!
//Set values by parsing files - determined at run-time
std::string param_string = "FavoriteFruit"; //this would be loaded from a file
std::string param_value = "grape"; //this would be loaded from a file
for (size_t i = 0; i < params.size(); i++) {
for (size_t j = 0; j < params[i].max_value; j++) {
if (params[i].paramName == param_string
&& params[i].str_ptr[j] == param_value) {
params[i].value = j;
break;
}
}
}
return 0;
}
如您所见,涉及枚举和字符串数组,它们需要匹配,因此出于维护目的,我需要将它们保存在同一个位置。此外,由于此代码已经编写并将在 Windows 和 Linux 环境中使用,因此修复越小越好。我宁愿不必为了让它在 Linux 中编译而重写数千行。谢谢!
看来您没有使用 C++17,而在 C++17 之前,这是未定义的行为,我非常不喜欢这种行为。你没有为你的s
定义,但你是ODR-using它,这意味着你需要有一个定义。
要定义它,您必须在 .cpp 文件中定义它。
在 C++17 中,这将是有效代码。我不熟悉 MSVC,所以我不确定为什么它在那里工作得很好——是因为它被编译为 C++17,还是因为它只是未定义行为的不同表现形式。
该程序在 C++17 中有效。该程序在 C++14 或更早的标准中无效。 GCC 6.5.0默认的标准模式是C++14.
为了使程序符合C++14,您必须定义静态成员(恰好在一个翻译单元中)。自 C++17 起,constexpr 声明隐含为内联变量定义,因此不需要单独定义。
解决方案 1:升级您的编译器并使用具有内联变量的 C++17 标准(或更高版本,如果您来自未来)。自 GCC 7 以来已经实现了内联变量。
解决方案2:在class定义之外的变量定义在一个翻译单元中(初始化保留在声明中)。
对于 C++98、C++11 和 C++14,您需要显式地告诉编译器 Foo::s 的初始化位置(见下文),您就可以开始了。
struct Foo{
static const char* s[];
};
const char* Foo::s[] = {"one","two","three"};
正如其中一条评论中所解释的那样,您的初始化从 C++17 就可以了。
2017 年 Visual Studio 一切正常,但我在 GCC (6.5.0) 中遇到链接器错误。
下面是一些可以解决我的问题的示例代码:
#include <iostream>
struct Foo{
static constexpr const char* s[] = {"one","two","three"};
};
int main(){
std::cout << Foo::s[0] << std::endl; //this works in both compilers
const char* const* str_ptr = nullptr;
str_ptr = Foo::s; //LINKER ERROR in GCC; works in VS
std::cout << str_ptr[1] << std::endl; //works in VS
return 0;
}
在 GCC 中,我得到 undefined reference to 'Foo::s'
。我需要 Foo::s
的初始化保留在结构的声明中,这就是我使用 constexpr
的原因。有没有办法动态引用 Foo::s
,即使用指针?
更多背景信息
我现在将解释为什么我想这样做。我正在开发嵌入式软件来控制设备的配置。软件加载包含参数名称及其值的配置文件。要配置的参数集在编译时确定,但需要模块化,以便随着设备的继续开发轻松添加新参数并扩展它们。换句话说,它们的定义需要在代码库中的一个地方。
我的实际代码库有数千行,可以在 Visual Studio 中运行,但这里有一个简化的玩具示例:
#include <iostream>
#include <string>
#include <vector>
//A struct for an arbitrary parameter
struct Parameter {
std::string paramName;
int max_value;
int value;
const char* const* str_ptr = nullptr;
};
//Structure of parameters - MUST BE DEFINED IN ONE PLACE
struct Param_FavoriteIceCream {
static constexpr const char* n = "FavoriteIceCream";
enum { vanilla, chocolate, strawberry, NUM_MAX };
static constexpr const char* s[] = { "vanilla","chocolate","strawberry" };
};
struct Param_FavoriteFruit {
static constexpr const char* n = "FavoriteFruit";
enum { apple, banana, grape, mango, peach, NUM_MAX };
static constexpr const char* s[] = { "apple","banana","grape","mango","peach" };
};
int main() {
//Set of parameters - determined at compile-time
std::vector<Parameter> params;
params.resize(2);
//Configure these parameters objects - determined at compile-time
params[0].paramName = Param_FavoriteIceCream::n;
params[0].max_value = Param_FavoriteIceCream::NUM_MAX;
params[0].str_ptr = Param_FavoriteIceCream::s; //!!!! LINKER ERROR IN GCC !!!!!!
params[1].paramName = Param_FavoriteFruit::n;
params[1].max_value = Param_FavoriteFruit::NUM_MAX;
params[1].str_ptr = Param_FavoriteFruit::s; //!!!! LINKER ERROR IN GCC !!!!!!
//Set values by parsing files - determined at run-time
std::string param_string = "FavoriteFruit"; //this would be loaded from a file
std::string param_value = "grape"; //this would be loaded from a file
for (size_t i = 0; i < params.size(); i++) {
for (size_t j = 0; j < params[i].max_value; j++) {
if (params[i].paramName == param_string
&& params[i].str_ptr[j] == param_value) {
params[i].value = j;
break;
}
}
}
return 0;
}
如您所见,涉及枚举和字符串数组,它们需要匹配,因此出于维护目的,我需要将它们保存在同一个位置。此外,由于此代码已经编写并将在 Windows 和 Linux 环境中使用,因此修复越小越好。我宁愿不必为了让它在 Linux 中编译而重写数千行。谢谢!
看来您没有使用 C++17,而在 C++17 之前,这是未定义的行为,我非常不喜欢这种行为。你没有为你的s
定义,但你是ODR-using它,这意味着你需要有一个定义。
要定义它,您必须在 .cpp 文件中定义它。
在 C++17 中,这将是有效代码。我不熟悉 MSVC,所以我不确定为什么它在那里工作得很好——是因为它被编译为 C++17,还是因为它只是未定义行为的不同表现形式。
该程序在 C++17 中有效。该程序在 C++14 或更早的标准中无效。 GCC 6.5.0默认的标准模式是C++14.
为了使程序符合C++14,您必须定义静态成员(恰好在一个翻译单元中)。自 C++17 起,constexpr 声明隐含为内联变量定义,因此不需要单独定义。
解决方案 1:升级您的编译器并使用具有内联变量的 C++17 标准(或更高版本,如果您来自未来)。自 GCC 7 以来已经实现了内联变量。
解决方案2:在class定义之外的变量定义在一个翻译单元中(初始化保留在声明中)。
对于 C++98、C++11 和 C++14,您需要显式地告诉编译器 Foo::s 的初始化位置(见下文),您就可以开始了。
struct Foo{
static const char* s[];
};
const char* Foo::s[] = {"one","two","three"};
正如其中一条评论中所解释的那样,您的初始化从 C++17 就可以了。