簡體   English   中英

constexpr真的需要嗎?

[英]Is constexpr really needed?

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

例如,以下代碼:

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函數。 但是,我想不出任何實際的應用程序,我曾想用這種復雜的計算來定義編譯時常量。

因此,即使它可能是一個有趣的功能,我仍然想知道是否需要它(即它可以解決宏不足的情況)。 也許看一些用宏無法解決的現實例子可以幫助我改變這種觀點。

int vec[s_maxSize]; 在第二個例子中實際上是非法的,因此在C ++中是不可能的。 但是你的第一個例子是完全合法的C ++ 0x。

所以有你的答案。 你實際上無法做你在C ++中提出的建議。 它只能在帶有constexpr C ++ 0x中完成。

我還想指出,這段代碼也適用於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 ++中的編譯器擴展來表達。

你可以用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");  
}

作為一個有用的例子,它可以促進編譯時計算/構造某些解析器和使用文字字符串指定的正則表達式有限狀態機。 您可以在編譯時推遲處理的處理越多,在運行時執行的處理就越少。

int MaxSize() {
    ...

    return ...; }

static const int s_maxSize = MaxSize();

int vec[s_maxSize];

不,它不能。 這不是合法的C ++ 03。 您有一個可以分配可變長度數組的編譯器擴展。

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 ++中的功能,但可能在許多編譯器中作為擴展。

一個更有趣的問題可能是:為什么不在每個(const)函數上放置constexpr 它有害嗎!?

另一個巧妙的技巧是constexpr允許在編譯時檢測未定義的行為 ,這看起來是一個非常有用的工具。 從我鏈接的問題中獲取的以下示例使用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

通過這種推理,你不需要一般的常量,甚至不需要#define 沒有內聯函數或任何東西。

像許多關鍵字一樣, constexpr 的目的是讓你更好地表達你的意圖,這樣編譯器就能准確理解你想要的東西,而不僅僅是你所說的,所以它可以在幕后為你做更好的優化。

在此示例中,它允許您編寫可維護的函數來計算矢量大小,而不僅僅是您反復復制和粘貼的純文本。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM