C++ Dilinde constexpr (2)
- Yusuf Hançar
- Feb 19
- 7 min read
İlk yazılarımızda C++ dilinde const ve constexpr ile ilgili inceleme yapmıştık. Ancak C++ standartlarının gelişimi ve yeni özellikleri söz konusu olduğu için güncel kalmak önem arz etmektedir. const, constexpr yanı sıra consteval, constinit gibi specifier eklentileri de incelenmelidir. Bu yazıda C++20 ile dile eklenen yeni constexpr özelliklerine değineceğiz.
Öncelikle C++11 ve C++20 arasındaki sürecine bakalım!

int main ()
{
constexpr int sc = 5;
}
*****************************
AÇIKLAMA : sc türü const int
constexpr int const sc = 5; gibi
*****************************
CEVAP :
*****************************
int main ()
{
int sc = 5;
constexpr int* p = ≻
}
*****************************
AÇIKLAMA : yerel değişkenlerin adresleri constant expression değildir.
*****************************
CEVAP : syntax error
*****************************
int sc = 5;
int main ()
{
constexpr int* p = ≻
}
*****************************
AÇIKLAMA : global değişkenlerin adresleri constant expression
*****************************
CEVAP : legal
*****************************
int x = 5;
int y{ 10 };
int main ()
{
constexpr int* p = &x;
p = &y;
}
*****************************
AÇIKLAMA :
*****************************
CEVAP : syntax error
*****************************
int sc = 5;
int main ()
{
constexpr int* p = ≻
*p = 10;
}
*****************************
AÇIKLAMA :
*****************************
CEVAP : legal
*****************************
int sc = 5;
int main ()
{
constexpr const int* p = ≻
*p = 10;
}
*****************************
AÇIKLAMA :
*****************************
CEVAP : syntax error
*****************************
constexpr member function implicitly const
class Data {
public :
constexpr int foo(int);
constexpr int foo(int) const; // bunu otomatik yapmış olurduk c++11 de
};
int main ()
{}
*****************************
AÇIKLAMA : C++’da aynı imzaya sahip constexpr fonksiyonları overload edemezsiniz.
c++11 den sonra legaldir.
*****************************
C++14 ve öncesinde, lambda ifadelerinin çağrı operatörü (operator()) otomatik olarak constexpr olmaz. Yani, bir lambda fonksiyonunu constexpr bağlamında kullanabilmek için açıkça constexpr olarak tanımlamak gerekir:
int main ()
{
auto fn = [](int sc) constexpr { return sc * 5; };
}
*****************************
AÇIKLAMA : ‘constexpr’ lambda only available with ‘-std=c++17’ or ‘-std=gnu++17’
C++17 öncesi error
*****************************
CEVAP : error
*****************************
int main ()
{
auto fn = [](int sc){ return sc * 5; };
constexpr int val = fn(10);
}
*****************************
AÇIKLAMA : c++17 öncesi syntax error çünkü üye fonksiyon constexpr değil.
*****************************
CEVAP :
*****************************
int foo(int val)
{
return val + 5;
}
constexpr int foo_1(int val)
{
return val + 6;
}
int main ()
{
constexpr int val = foo(10); // syntax error
constexpr int val = foo_1(10); // legal
}
hem compile hem de run-time da çağırabilen constexpr fonksiyon..
constexpr int foo(int val)
{
return val + 6;
}
int main ()
{
constexpr int ctime = foo(10); // Compile-time evaluation
//
int rtime = 5;
int result = foo(rtime); // Run-time evaluation
}
constexpr int foo(int val)
{
return val + 6;
}
int main ()
{
foo(10);
}
*****************************
AÇIKLAMA : bu fonksion hem run-time hem de compile-time da çağırılabilir.
bunun compile time da çağırılma garantisinin olabilmesi için geçilen parametrenin constant expression olması yeterli değildir.
fonksiyon çağrı ifadesinin (foo(10)) constant expression gereken bir bağlamda kullanılması gerekir.
*****************************
C++20 ile dile eklenen bazı araçlarla kullanıldığında compile-time da çağırılıp çağırılma garantisi olup olmaması önem taşır.
sabit ifadesi olan yerde ayrı bir optimizasyon
sabit ifadesi olmayan yerde ayrı bir optimizasyon
is_constant_evaluated()
in type_traits header file
#include <iostream>
#include <type_traits>
constexpr int foo(int val)
{
if (std::is_constant_evaluated())
{
return val * 5;
}
else
{
return val * 10;
}
}
int main ()
{
int val = foo(10);
std::cout << "val : " << val << "\n";
}
*****************************
AÇIKLAMA : c++20
*****************************
CEVAP : val : 100
*****************************
constexpr int foo(int val)
{
if (std::is_constant_evaluated())
{
return val * 5;
}
else
{
return val * 10;
}
}
int main ()
{
constexpr int val = foo(10);
std::cout << "val : " << val << "\n";
}
*****************************
AÇIKLAMA : Compile-Time Evaluation with Conditional Multiplication in C++20
*****************************
CEVAP : val : 50
*****************************
constexpr int foo(int val)
{
if (std::is_constant_evaluated())
{
return val * 5;
}
else
{
return val * 10;
}
}
int main ()
{
int arr[foo(10)]{};
}
*****************************
AÇIKLAMA : Fonksiyon, `std::is_constant_evaluated()` fonksiyonunu kullanarak derleme zamanında mı yoksa çalışma zamanında mı değerlendirildiğini kontrol eder. Eğer derleme zamanında değerlendirilirse, `x` değerini 5 ile çarpar; aksi takdirde 10 ile çarpar. `main` fonksiyonu içinde `arr` adlı bir dizi tanımlanır ve boyutu `foo(10)` ifadesiyle belirlenir. Derleme zamanında değerlendirildiği için `arr` dizisinin boyutu 50 olur.
*****************************
CEVAP : arr dizisinin boyutu 50
*****************************
constexpr int foo(int val)
{
if (std::is_constant_evaluated())
{
return val * 5;
}
else
{
return val * 10;
}
}
int main ()
{
std::array<int, foo(5)> arr;
}
*****************************
AÇIKLAMA : 2.template parametresi none-type parameter
`constexpr int foo(int x)` fonksiyonu, `std::is_constant_evaluated()` kontrolü sayesinde derleme zamanında veya çalışma zamanında farklı sonuçlar döndürür. Derleme zamanında çağrıldığında `x * 5` döner, aksi halde `x * 10` döner. `main` fonksiyonunda, `foo(5)` çağrısı derleme zamanında yapıldığı için `std::array arr;` ifadesi `25` boyutunda bir dizi oluşturur.
*****************************
CEVAP : 25
*****************************
if constexpr
#include <iostream>
#include <type_traits>
constexpr int foo(int val)
{
if (std::is_constant_evaluated())
{
return val * 5;
}
else
{
return val * 10;
}
}
int main ()
{
if constexpr (foo(5) > 30)
{
std::cout << "bigger" << "\n";
}
else
{
std::cout << "smaller" << "\n";
}
}
*****************************
AÇIKLAMA :
*****************************
CEVAP : bigger
*****************************
static_assert ile test edebiliriz...
constexpr int foo(int val)
{
if (std::is_constant_evaluated())
{
return val * 5;
}
else
{
return val * 10;
}
}
int main ()
{
static_assert(foo(5) == 25);
}
std::bitset ile kullanımı
#include <iostream>
#include <type_traits>
#include <bitset>
constexpr int foo(int val)
{
if (std::is_constant_evaluated())
{
return val * 5;
}
else
{
return val * 10;
}
}
int main ()
{
std::bitset<foo(10)> bs;
}
*****************************
AÇIKLAMA : bu da sabit ifadesi gereken bir bağlamdır.
`std::bitset bs;` ifadesi kullanılır ve `foo(10)` derleme zamanında değerlendirildiği için `bitset<50>` oluşturulur.
*****************************
CEVAP : bitset<50> bs
*****************************
int foo(int idx)
{
int arr[] = { 1, 3, 5, 8, 44 };
return arr[idx];
}
int main ()
{
int val = foo(20);
}
*****************************
AÇIKLAMA : dizi taşması
`foo` fonksiyonu 20 argümanıyla çağrıldığında, dizinin yalnızca 5 elemanı olduğu için dizinin sınırlarının dışına çıkma (out-of-bounds access) durumu meydana gelir.
*****************************
CEVAP : undefined behaviour
*****************************
int foo(int idx)
{
int arr[] = { 1, 3, 5, 8, 44 };
return arr[idx];
}
int main ()
{
constexpr int val = foo(20);
}
*****************************
AÇIKLAMA : constexpr değişkeni initialize eden ifadenin constant expression olması gerek ancak foo öyle değil
*****************************
CEVAP : syntax error
*****************************
constexpr int foo(int idx)
{
int arr[] = { 1, 3, 5, 8, 44 };
return arr[idx];
}
int main ()
{
constexpr int val = foo(20);
}
*****************************
AÇIKLAMA : constexpr fonksiyon compile-time bağlamında çağırmaya zorlanırsa derleyici tespit ettiği tanımsız davranışı edinip syntax error olarak değerlendirmek zorundadır.
*****************************
CEVAP : syntax error
*****************************
using throw statement
c++14
throw statement kullanılabilmesi için compile-time değil run-time contextinde çağırılması gerekir.
constexpr fonksiyon içinde throw statement olması constexpr koşulunu ezmez.
constexpr int fact(int n)
{
if (n < 0)
{
throw std::runtime_error{ "negative fact argument" };
}
int ret{ 1 };
for (int i{1}; i <= n; ++i)
{
ret *= i;
}
return ret;
}
int main ()
{
int val;
std::cout << "sayi gir :";
std::cin >> val;
try
{
auto ret = fact(val);
}
catch(const std::exception& ex)
{
std::cout << "exception caught : " << ex.what() << "\n";
}
}
*****************************
AÇIKLAMA : eğer bu kuralı çiğneseydi fonksiyon tanımına syntax error verirdi.
run-time context te çağırılırsa exception throw eder,
*****************************
CEVAP : legal
*****************************
constexpr int fact(int n)
{
if (n < 0)
{
throw std::runtime_error{ "negative fact argument" };
}
int ret{ 1 };
for (int i{1}; i <= n; ++i)
{
ret *= i;
}
return ret;
}
int main ()
{
int sc;
std::cout << "sayi gir :";
std::cin >> sc;
constexpr auto ival = fact(sc); // error
}
*****************************
AÇIKLAMA : compile-time context te çağırılırsa syntax error olur,
*****************************
CEVAP : syntax error
*****************************
algoritmaların tamamına yakına constexpr
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
constexpr int foo(int n)
{
int arr[6] = { 5, 8, 2, 4, 9, 17 };
std::sort(begin(arr), end(arr), std::greater{});
return arr[n];
}
int main ()
{
constexpr auto val = foo(2);
std::cout << "val : " << val << "\n";
}
*****************************
AÇIKLAMA : c++20
sort constepxr fonksiyon olduğu için ve sabit ifadesiyle ilk değer verip bu bağlamda kullandık
*****************************
CEVAP : val burada 8 compile-time da hesaplandı
*****************************
#include <iostream>
#include <algorithm>
#include <functional>
#include <numeric>
using namespace std;
constexpr int foo(int x, int y)
{
int arr[6] = { 5, 8, 2, 4, 9, 17 };
return std::accumulate(next(begin(arr), x), next(begin(arr), y), 0);
}
int main ()
{
constexpr auto val = foo(2, 5);
std::cout << "val : " << val << "\n";
}
*****************************
AÇIKLAMA : c++20
5 end konumu 2 begin konumu yani
2 begin ve 4 ve 9 ise 3ve 4. olanlar 5 end olduğu için almadık
2 + 4 + 9 = 15
*****************************
CEVAP : 15
*****************************
constexpr int foo(int n)
{
std::vector vec{ 1, 4, 8, 4, 6, 8 };
return std::accumulate(vec.begin(), next(vec.begin(), n), 0);
}
int main ()
{
constexpr int v = foo(3);
}
*****************************
AÇIKLAMA : c++20
*****************************
CEVAP : 1 + 4 + 8
*****************************
constexpr int get_median(std::vector<int> vec)
{
std::sort(vec.begin(), vec.end());
return vec[vec.size() / 2];
}
int main ()
{
constexpr int med = get_median({ 4, 8, 52, 41, 7, 36, 85, 11, 9 });
}
*****************************
AÇIKLAMA : c++20
*****************************
#include <vector>
#include <string>
#include <algorithm>
#include <iostream>
constexpr std::vector<std::string> split(std::string_view strv, std::string_view delims = " ")
{
std::vector<std::string> output;
size_t first = 0;
while (first < strv.size())
{
const auto second = strv.find_first_of(delims, first);
if (first != second)
{
output.emplace_back(strv.substr(first, second-first));
}
if (second == std::string_view::npos)
{
break;
}
first = second + 1;
}
return output;
}
constexpr size_t numWords(std::string_view str)
{
const auto words = split(str);
return words.size();
}
int main()
{
static_assert(numWords("hello world abc xyz") == 4);
constexpr size_t val = numWords("hello world abc xyz ali");
}
*****************************
AÇIKLAMA : c++20
yazıdaki kelime sayısını alır.
*****************************
CEVAP : 5
*****************************
#include <vector>
#include <numeric>
#include <algorithm>
struct Point {
float x, y;
constexpr Point& operator+=(const Point& a) noexcept
{
x += a.x;
y += a.y;
return *this;
}
};
constexpr bool testVector(int n)
{
std::vector<Point*> vec(n);
for (auto& pt : vec) {
pt = new Point;
pt->x = 0.0f;
pt->y = 1.0f;
}
Point sumPt { 0.0f, 0.0f};
for (auto &pt : vec)
{
sumPt += *pt;
}
for (auto& pt : vec)
{
delete pt;
}
return static_cast<int>(sumPt.y) == n;
}
int main()
{
static_assert(testVector(10));
}
*****************************
AÇIKLAMA : c++20
testVector constexpr olmazsa error
*****************************
CEVAP : legal
*****************************
Comments