16

我一直在constexpr研究 C++ 的新特性,但我并不完全理解它的必要性。

例如,下面的代码:

constexpr int MaxSize()
{
    ...

    return ...;
}

void foo()
{
    int vec[MaxSize()];
}

可以替换为:

int MaxSize()
{
    ...

    return ...;
}

static const int s_maxSize = MaxSize();

foo()
{
    int vec[s_maxSize];
}

更新

第二个示例实际上不是标准的 ISO C++(感谢一些用户指出这一点),但某些编译器(例如 gcc)支持它。因此,这并不是const使程序有效,而是 gcc 支持这种非标准功能的事实。(据我所知,这只有在数组被定义为函数或方法的本地时才有可能,因为在编译时仍然必须知道全局数组的大小。)如果我在没有选项-std=c++98 -pedantic-errors的情况下编译,甚至代码

int MaxSize()
{
    return 10;
}

void foo()
{
    int vec[MaxSize()];
}

将使用 gcc 编译。

因此,我将尝试重新表述我的问题,同时考虑到到目前为止的反馈(以及我在此期间所做的一些进一步阅读)。

const大量使用关键字。我可以定义一个在const其整个生命周期内具有特定值的常量。可以使用任何表达式初始化常量,该表达式只计算一次,即在创建常量时。对于这些情况,我认为这constexpr毫无用处:它会引入一个非常小的优化,因为定义常量值的表达式将在编译时而不是运行时计算。每次我需要一个带有复杂初始化的运行时常量时,我​​都会使用关键字const.

所以constexpr在我们需要在编译时初始化一个常量的情况下可能会派上用场。一个例子是向量定义:标准不支持在运行时定义大小。另一个示例是具有一个或多个非类型参数的模板。

在这种情况下,我通常使用宏:

#define MAX_SIZE (10)

void foo()
{
    int vec[MAX_SIZE];
}

但是,如果我理解正确,constexpr函数比宏更强大,因为它们允许constexpr在其定义中递归调用函数。但是,我想不出任何实际应用程序中我曾经想使用如此复杂的计算来定义编译时常量。

因此,即使它可能是一个有趣的功能,我仍然想知道它是否需要(即它多久可以解决宏不够用的情况)。也许看看一些无法用宏解决的现实例子会帮助我改变这种看法。

4

6 回答 6

21

int vec[s_maxSize];在第二个例子中实际上是非法的,所以这在 C++ 中是不可能的。但是您的第一个示例是完全合法的 C++0x。

所以这就是你的答案。你实际上不能做你在 C++ 中提出的建议。它只能在 C++0x 中使用constexpr.

我还想指出,这段代码也适用于 C++0x。在 C++ 中这样做需要一些非常花哨的类模板。

constexpr unsigned int gcd(unsigned int const a, unsigned int const b)
{
   return (a < b) ? gcd(b, a) : ((a % b == 0) ? b : gcd(b, a % b));
}

char vec[gcd(30, 162)];

当然,在 C++0x 中,您仍然必须使用三元运算符而不是 if 语句。但是,它可以工作并且仍然比您在 C++ 中强制使用的模板版本更容易理解:

template <unsigned int a, unsigned int b>
class gcdT {
 public:
   static unsigned int const value = gcdT<b, a % b>::value;
};

template <unsigned int a>
class gcdT<a, 0> {
 public:
   static unsigned int const value = a;

};

char vec[gcdT<30, 162>::value];

然后,当然,在 C++ 中,如果您需要在运行时计算事物,您仍然必须编写gcd函数,因为模板不能与在运行时变化的参数一起使用。并且 C++0x 将有额外的优化提升,因为知道函数的结果完全由传入的参数决定,这是只能用 C++ 中的编译器扩展来表达的事实。

于 2011-06-25T05:25:00.053 回答
7

你可以用 constexpr 做而不能用宏或模板做的事情是在编译时解析/处理字符串:使用 constexpr 进行编译时字符串处理(更改大小写、排序等)。作为上述链接的一小段摘录, constexpr 允许人们编写如下代码:

#include "my_constexpr_string.h"
int main()
{
   using namespace hel;
   #define SDUMP(...) static_assert(__VA_ARGS__, "")

   SDUMP(tail("abc") == "bc");
   SDUMP( append("abc", "efgh") == "abcefgh" );
   SDUMP( prepend("abc", "efgh") == "efghabc" );
   SDUMP( extract<1,3>("help") == "el" );
   SDUMP( insert<1>("jim", "abc") == "jabcim" );
   SDUMP( remove("zabzbcdzaz", 'z') == "abbcdazzzz" );
   SDUMP( erase("z12z34z5z", 'z') == "12345"  );
   SDUMP( map("abc", ToUpper()) == "ABC" );
   SDUMP( find("0123456777a", '7') == 7 );
   SDUMP( isort("03217645") == "01234567");  
}

作为何时有用的示例,它可以促进某些解析器和正则表达式有限状态机的编译时计算/构造,这些解析器是用文字字符串指定的。而且,您可以在编译时进行的处理越多,您在运行时执行的处理就越少。

于 2011-07-24T15:23:42.903 回答
3
int MaxSize() {
    ...

    return ...; }

static const int s_maxSize = MaxSize();

int vec[s_maxSize];

不,它不能。这不是合法的 C++03。您有一个可以分配可变长度数组的编译器扩展。

于 2011-06-25T11:11:24.277 回答
1

constexpr允许 if 在编译时检测未定义行为的另一个巧妙技巧,这看起来是一个非常有用的工具。以下示例取自我链接的问题,使用SFINAE检测添加是否会导致溢出:

#include <iostream>
#include <limits>

template <typename T1, typename T2>
struct addIsDefined
{
     template <T1 t1, T2 t2>
     static constexpr bool isDefined()
     {
         return isDefinedHelper<t1,t2>(0) ;
     }

     template <T1 t1, T2 t2, decltype( t1 + t2 ) result = t1+t2>
     static constexpr bool isDefinedHelper(int)
     {
         return true ;
     }

     template <T1 t1, T2 t2>
     static constexpr bool isDefinedHelper(...)
     {
         return false ;
     }
};


int main()
{    
    std::cout << std::boolalpha <<
      addIsDefined<int,int>::isDefined<10,10>() << std::endl ;
    std::cout << std::boolalpha <<
     addIsDefined<int,int>::isDefined<std::numeric_limits<int>::max(),1>() << std::endl ;
    std::cout << std::boolalpha <<
      addIsDefined<unsigned int,unsigned int>::isDefined<std::numeric_limits<unsigned int>::max(),std::numeric_limits<unsigned int>::max()>() << std::endl ;
}

这导致(现场观看):

true
false
true
于 2014-01-27T03:46:01.860 回答
0

constexpr允许以下工作:

#include<iostream>
using namespace std;

constexpr int n_constexpr() { return 3; }
int n_NOTconstexpr() { return 3; }


template<size_t n>
struct Array { typedef int type[n]; };

typedef Array<n_constexpr()>::type vec_t1;
typedef Array<n_NOTconstexpr()>::type vec_t2; // fails because it's not a constant-expression

static const int s_maxSize = n_NOTconstexpr();
typedef Array<s_maxSize>::type vec_t3; // fails because it's not a constant-expression

template参数确实需要是常量表达式。您的示例有效的唯一原因是可变长度数组 (VLA) - 标准 C++ 中没有的功能,但可能在许多编译器中作为扩展。

一个更有趣的问题可能是:为什么不把constexpr每个 (const) 函数都放在上面呢?有没有害处!?

于 2013-10-28T21:25:44.347 回答
-4

通过这种推理,您通常不需要常量,甚至不需要#define. 没有内联函数或任何东西。

就像许多关键字一样,它的目的constexpr是让您更好地表达您的意图,以便编译器准确理解您想要的内容,而不是您告诉它的内容,以便它可以在幕后为您进行更好的优化。

在此示例中,它允许您编写可维护的函数来计算矢量大小,而不仅仅是您一遍又一遍地复制和粘贴的纯文本。

于 2011-06-25T05:13:31.407 回答