无法声明静态constexpr字符数组

5

我已经阅读了所有与此问题相关的答案,但老实说,我不确定我是否完全理解了解决方案。我正在使用C++11。

假设我真的想声明像 static constexpr char value[] = "foo" 这样的东西。

如果我使用 NetBeans/TDM_MINGW,我会得到一个错误,我认为这是一个链接错误,报告未定义引用“变量名”

在 MS VS 2015 中尝试相同的代码,我得到"表达式未评估为常量"

一个简单的 static constexpr char *可以解决问题,但我失去了使用 sizeof 等表达式的能力。

简单明了的问题(如果可能的话,是明确的答案):

  1. 有没有一种方法在 struct / class 内声明 static constexpr char []
  2. 如果1)是错误的,是否有更清洁的解决方案来克服这个问题? static constexpr char *????
  3. 还是旧的 static const char []仍然是这种情况下最好的方法?
  4. 我测试了一个可行的解决方案,但远非“干净” static constexpr array<char,50> getConstExpr(){ return array<char,50> {"Hell"} }。它可以正常工作,但我必须声明std::array的字符大小:(

嗯,我已经使用mingw64(4.9.2)和相同版本的gcc进行了测试,那一行代码可以正常工作。是C++11支持有限的问题吗?你能给出你所阅读的参考资料吗?静态constexpr成员变量只在C++14之后才被支持。 - Swift - Friday Pie
3个回答

4

1) 如何在struct/class中声明static constexpr char []?

可以通过下面的方法实现:

以下是一个完整的工作示例:

struct bar
 { static constexpr char value[] = "foo"; };

constexpr char bar::value[];

int main ()
 {
   std::cout << bar::value << std::endl; // print foo
 }

我猜您忘记了 bar::value[] 行。

2) 如果 1) 是假的,有没有更好的解决办法来克服这个 static constexpr char * ?

不适用。

3) 或者对于这种情况,旧的 static const char [] 仍然是最好的方法吗?

取决于您要解决的问题;但通常我建议避免使用 C 风格数组,并使用新的 C++11 std::array

4) 我测试了一个可以工作但远非“干净”的解决方案[...] 它完美地工作,但我必须声明 char 的大小 std::array :(

我向您提出一个解决方案(不幸的是,它从 C++14 开始工作,但制作一个 C++11 版本并不太困难),以便从传递的参数“Hell”中检测正确的大小。

观察使用 std::make_index_sequencestd::index_sequencechar[] 变量的单个字符传递给 std::array

template <std::size_t Dim, std::size_t ... Is>
constexpr std::array<char, Dim> gceH (char const (&str)[Dim],
                                      std::index_sequence<Is...> const &)
 { return { { str[Is]... } }; }

template <std::size_t Dim>
constexpr std::array<char, Dim> getConstExpr (char const (&str)[Dim])
 { return gceH(str, std::make_index_sequence<Dim>{}); }

int main ()
 {
   constexpr auto f = getConstExpr("Hell");

   static_assert( 5U == f.size(), "!" );
 }

--编辑--

根据Swift的建议(谢谢!),使用模板类型代替char,将getConstExpr()转换为更灵活的函数。

因此,getConstExpr()和辅助函数(gceH())可以编写如下:

template <typename T, std::size_t Dim, std::size_t ... Is>
constexpr std::array<T, Dim> gceH (T const (&str)[Dim],
                                   std::index_sequence<Is...> const &)
 { return { { str[Is]... } }; }

template <typename T, std::size_t Dim>
constexpr std::array<T, Dim> getConstExpr (T const (&str)[Dim])
 { return gceH(str, std::make_index_sequence<Dim>{}); }

把字符类型的数组元素也作为模板参数会很好,这样不是可以重用模板吗? - Swift - Friday Pie
感谢您的客观回答 @max66。我确实忘记在类范围外声明它。很高兴有你在身边。 - Jacinto Resende
@Swift - 为什么不呢?根据您的建议修改了答案。 - max66
@JacintoResende - 根据Swift的建议,答案得到了改进。 - max66
在C++17中,不再需要在类范围之外声明它。MS VS Community 2017支持它并且可以免费个人使用。在Linux上,clang版本5.0也支持它。 - S.Clem

1

自 C++17 起,您无需采取任何特殊措施,因为 static constexpr 成员变量会被隐式地视为 inline

以下代码可以正常工作:

#include <iostream>

struct S
{
    static constexpr char value[] = "Meow!\n";
};

int main()
{
    std::cout << S::value;
}

0
如果你想避免使用C风格的数组,也可以使用C++17中的std::string_view解决方案...这更加简单。 ;)
以下是以下代码的perm-link
#include <iostream>
#include <string_view>

int main ()
{
    constexpr std::string_view foo ( "Hell" );
    static_assert ( 4U == foo.size (), "!" );

    std::cout << "Done";
    return EXIT_SUCCESS;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接