Constexpr Anahtar Sözcüğü

If there were an award for the most confusing new word in C++11, constexpr would probably win it. -Scott Meyers

constexpr (konstekspra, diye okunur) anahtar sözcüğü C++11 ile tanıtıldı, Standard Komitesi daha önce hiçbir kod tabanında kullanılmadığından emin oldukları, constant expression kelimelinin kısaltması olan bu garip kelimeyi geliştirdi.

Peki ne işe yarar ve neden ihtiyaç duyuldu?

Son yıllarda programları koşma zamanından (run time), derleme zamanına (compile time) yığma gibi bir eğilim var. Çünkü programlar bir kere derlenir ve defalarca çalışır. Bir kere hesaplanması veya oluşturulması gereken şey, neden tekrar tekrar hesaplansın. Ayrıca bir hata varsa görmek için neden programın çalışmasını bekleyelim?

Bu yaklaşımın artıları:

  • Programın çalışma hızını artırması
  • Programın çalışma zamanını azaltması
  • Programın daha az binary üretmesi
  • Hataların daha çabuk bulunması

Eksileri:

  • Çalıştırılabilir dosya(executable) boyutunu artırması
  • Derleme zamanını artırması

constexpr anahtar sözcüğü işaretlendiği nesnenin derleme zamanında çalışmasını sağlar, ama her zaman değil😄

constexpr sabitler:

         constexpr auto arraySize{10};                                                                                                                                                    
         std::array<int, arraySize> a{};                                                                       
         static_assert(arraySize == a.size());   

arraySize değişkeni derleme zamanında bilindiği için array tutucusunun boyunu belirtmede kullanılabiliyor.

Tüm tutucuların (container) boyu constexpr olarak bildirilmeli.

constexpr fonksiyonlar

	int square(int i) noexcept { return i * i; }
	std::array<int, square(10)> a{};

yukarıdaki kod derleme zamanı hatasına neden olur.

 error: non-type template argument is not a constant expression
        std::array<int, square(10)> a{};
                        ^~~~~~~~~~

Çözüm için square fonksiyonu constexpr olarak işaretlemeli:

	constexpr int square(int i) noexcept { return i * i; }
	std::array<int, square(8)> a{};

Ama bir fonksiyonu constexpr olarak işaretlemek onun derleme zamanında çalıştırılacağını garanti etmiyor, constexpr fonksiyona bir veya daha fazla derleme zamanında bilinmeyen parametre geçildiğinde, dermeleme zamanında (compile time), koşma zamanında (run time) çalışacaktır:

	constexpr int square(int i) noexcept { return i * i; }

	int k;
	std::cin >> k;

	std::cout << square(100); // square fonk. derleme zamanından koşma zamanına sarkar

Buradaki güzellik derleme zamanı ve koşma zamanı için iki farklı fonksiyon yazmamıza gerek olmaması. Eğer bir constexpr fonksiyon derleme zamanında çağrılmadıysa koşma zamanında çalışacaktır. İleride pek çok Standard Kütüphane fonksiyonunun default olarak constexpr olabileceği söyleniyor.

Fonksiyonların constexpr olması için literal tipleri (veya referanslarını) argüman olarak alması ve/veya döndürmesi gereklidir.

literal tipler hangileridir?

Derleme zamanında boyu değişmeyen tipler…

constexpr std::string greet(std::string const& name) { return "Hello " + name; }

std::cout << greet("Adem");
Error: constexpr function's return type 'std::string' (aka 'basic_string<char>') is not a literal type
constexpr std::string greet(std::string const& name) { return "Hello " + name; }
                      ^ 

std::string literal bir tip değildir.

C++11’e kadar constexpr fonksiyonlar yalnızca bir adet return ifadesine sahip olabilirdi, C++14 bu kural esnetildi.

// C++11
constexpr int factorial(int n) {
	return 1 ? n <= 1 : n * factorial11(n - 1);
}
// C++14
constexpr int factorial(int n) {
	if (n <= 1)
           return 1;
	return n * factorial(n - 1);
}

İçerisinde hafızadan yer ayırmayan, zahmetsizce yokedilebilen sınıfların üye foksiyonları da constexpr olarabiiir.

Bu zahmetsiz yokedilebilirliği <type_traits> başlığındaki is_trivially_destructable fonksiyonuyla kontrol ederiz.

const ve constexpr

Tüm constexpr ifadeler implicit olarak const’tur, ama tersi doğru değildir.

constexpr üye fonksiyonlar

class Integer {
       private:
	int m_val{};

       public:
	constexpr Integer(int t_val) noexcept : m_val{t_val} {}
	constexpr int getVal() const noexcept { return this->m_val; }
	void setVal(const int t_val) noexcept { this->m_val = t_val; }
	constexpr Integer operator+(const int t_val) const noexcept {
		return Integer{this->m_val + t_val};
	}
	constexpr Integer operator+(Integer rhs) const noexcept {
		return Integer{this->m_val + rhs.m_val};
	}
};
	std::cout << std::is_trivially_destructible<Integer>::value;
	//Output:
	1	

C++11’e göre objenin durumun değiştiren setter fonkiyon constexpr olamıyor, fakat C++14 bu kısıtı kaldırdı:

class Integer {
       private:
	int m_val{};

       public:
	constexpr Integer(int t_val) noexcept : m_val{t_val} {}
	constexpr auto getVal() const noexcept { return this->m_val; }
	constexpr void setVal(const int t_val) noexcept { this->m_val = t_val; }
	constexpr auto operator+(const int t_val) const noexcept {
		return Integer{this->m_val + t_val};
	}
	constexpr auto operator+(Integer rhs) const noexcept {
		return Integer{this->m_val + rhs.m_val};
	}
};
constexpr Integer a(10);                                                                                         
constexpr Integer b(20);                                                                                         
constexpr Integer c = a + b;   

Bu kodun tamamı derleme zamanında çalışacağı için bir assembly üretmez.

Lambda ifadeleri implicit olarak constexpr’dır.


  1. Item 15: Use constexpr whenever possible, Page 97-103, Modern Effective C++, S. Meyers
  2. Scott Schurr’s talk at CppCon 2017 1 2
  3. Practical constexpr by Jason Turner, Meeting C++ 2017
  4. https://isocpp.org/wiki/faq/cpp14-language#extended-constexpr
  5. https://github.com/AnthonyCalandra/modern-cpp-features