ãã®ããŒãžã¯C++26ã«æ¡çšãããèŠèŸŒã¿ã®èšèªæ©èœã®å€æŽã解説ããŠããŸãã
ã®ã¡ã®C++èŠæ Œã§ããã«å€æŽãããå Žåãããããé¢é£é ç®ãåç §ããŠãã ããã
æŠèŠ
C++26ã§ã¯ã颿°ã®æ£ç¢ºãªåäœãæç€ºçã«æå®ã§ããããã°ã©ã ã®æ£åœæ§ãé«ããããã«ãå¥çŽããã°ã©ãã³ã°ãæ©èœãå°å ¥ãããã
ããã«ãããäºåæ¡ä»¶(preconditions)ãäºåŸæ¡ä»¶(postconditions)ãåã³ã¢ãµãŒã·ã§ã³(assertions)ãã³ãŒãå ã§æç€ºçã«èšè¿°ã§ããããã«ãªãã
ãã®æ©èœã¯ã颿°ã®ã€ã³ã¿ãŒãã§ãŒã¹ã«å¯ŸããæåŸ å€ãæç¢ºã«ãã圹å²ãããããã°ã®æ©æçºèŠãã³ãŒãã®å¯èªæ§åäžã«å¯äžããããšãæåŸ ãããŠããã
仿§
ããŒã¯ãŒã
preãpostã¯æèäŸåããŒã¯ãŒãã§ããããããã¯overrideãfinalãšåæ§ã«ãç¹å®ã®æèã§ã®ã¿ç¹å¥ãªæå³ãæã€ãcontract_assertã¯å®å
šãªããŒã¯ãŒãã§ããã
preãšpostã¯å€æ°åã颿°åãšããŠäœ¿çšå¯èœ
int pre = 42; // OK: 倿°åãšããŠäœ¿çš void post() {} // OK: 颿°åãšããŠäœ¿çš- å¥çŽæå®ã®æèã§ã®ã¿ç¹å¥ãªæå³ãæã€
void f() pre(true); // ããã§ã¯å¥çŽæå®ãšããŠæ©èœ contract_assertã¯æ¢åã®assertãã¯ããšã®è¡çªãé¿ããããã®å®å šãªããŒã¯ãŒã
void g() { contract_assert(true); // ã¢ãµãŒã·ã§ã³æ }
å¥çŽã®çš®é¡
å¥çŽã«ã¯ä»¥äžã®3çš®é¡ãå®ããããŠããã
- äºåæ¡ä»¶(preconditions)
- äºåŸæ¡ä»¶(postconditions)
- ã¢ãµãŒã·ã§ã³(assertions)
äºåæ¡ä»¶(pre)
颿°ãåŒã³åºãããåã«æºããããŠããã¹ãæ¡ä»¶ãæå®ããã
int safe_division(int numerator, int denominator)
pre(denominator != 0)
{
return numerator / denominator;
}
denominatorã0ã§ãªãããšãäºåæ¡ä»¶ãšããŠæå®ããŠããã
äºåæ¡ä»¶ã§ã¯ãå¿ ããã颿°ã®ãã©ã¡ãŒã¿ã䜿çšããå¿ èŠã¯ãªããã°ããŒãã«ç¶æ ãã¯ã©ã¹ã®ã¡ã³ã倿°ãªã©ãåŒã³åºãæç¹ã§æå¹ãªä»»æã®åŒã䜿çšã§ããã
class Resource {
private:
bool is_available = false;
public:
void use()
pre(is_available) // åŒæ°ã䜿çšããŠããªãäºåæ¡ä»¶
{
// ãªãœãŒã¹ã䜿çš
}
void activate() {
is_available = true;
}
};
äºåŸæ¡ä»¶(post)
颿°ã®å®è¡åŸã«æºããããŠããã¹ãæ¡ä»¶ãæå®ããã
int increment(const int x)
post(r: r == x + 1)
{
return x + 1;
}
increment颿°ã®æ»ãå€ãx + 1ã§ããããšãäºåŸæ¡ä»¶ãšããŠæå®ããŠããã
äºåŸæ¡ä»¶ã«é¢æ°åŒæ°ã䜿çšããå Žåã䜿çšããåŒæ°ã¯åç
§åããconst修食åã§ãªããã°ãªããªãã
postã§ã¯ãæ»ãå€ãrãšããŠãã€ã³ãããæ¡ä»¶åŒå
ã§å©çšããŠãããããã«ã¯ãä»»æã®å€æ°åã䜿çšã§ããã倿°ã¯å®æ°(const)ãªå·ŠèŸºå€åç
§ã§ããã
äºåŸæ¡ä»¶ã®çµæåå°å
¥åïŒresult-name-introducerïŒã¯çç¥å¯èœã§ãããç¹ã«ãvoidãè¿ã颿°ã§ã¯ãæ»ãå€ãåç
§ããå¿
èŠããªãå Žåã«çç¥ã§ããã
class Container {
// ...
public:
void clear()
post(empty()) // çµæåå°å
¥åãçç¥ããäºåŸæ¡ä»¶
{
// ã³ã³ããã®å
容ãã¯ãªã¢
}
bool empty() const {
// ã³ã³ããã空ãã©ãããè¿ã
return true;
}
};
ã¢ãµãŒã·ã§ã³(assert)
颿°ã®å®è¡äžã«æºããããŠããã¹ãæ¡ä»¶ãæå®ããã
int return_negative(int value)
{
contract_assert(value >= 0);
return -value;
}
return_negative颿°ãåŒæ°ãšããŠåãåã£ãŠããvalueã0以äžã§ããããšãã¢ãµãŒã·ã§ã³ãšããŠæå®ããŠããã
contract_assertã¯ã颿°ã®æ¬äœå
ã§äœ¿çšãããã
ãŸãããããã®å
šãŠã¯ã[[ likely ]]ã[[ unlikely ]]ã [[ maybe_unused ]]屿§ã䜿çšããããšãã§ããã
int return_negative(int value)
pre [[likely]] (value >= 0)
post (r [[maybe_unused]] : r <= 0)
{
return -value;
}
æ§æäžã®å¶çŽ
å¥çŽããã°ã©ãã³ã°ã«ã¯ãããã€ãã®æ§æäžã®å¶çŽãããã
è€æ°ã®å®£èš
颿°ã«è€æ°ã®å®£èšãããå Žåãããããã¹ãŠã«åãå¥çŽæå®åã®äžŠã³ãæå®ããããšããäžéšã®å®£èšã«ã®ã¿æå®ããããšãã§ãããå¥çŽæå®åã®äžŠã³ãæå®ãããŠããªã宣èšã¯ãä»ã®å®£èšããå¥çŽã¢ãµãŒã·ã§ã³ãç¶æ¿ããã
int f(int x) pre(x > 0); // 宣èš
int f(int x); // OK: å¥çŽã¢ãµãŒã·ã§ã³ãç¶æ¿
int f(int x) pre(x > 0) { // OK: å®çŸ©ã§ãåãå¥çŽãæå®
return x * 2;
}
ä»®æ³é¢æ°
ä»®æ³é¢æ°ã«äºåæ¡ä»¶ãŸãã¯äºåŸæ¡ä»¶ã®æå®åãä»ããããšã¯äžé©æ ŒïŒill-formedïŒã§ããã
struct Base {
// ãšã©ãŒïŒä»®æ³é¢æ°ã«å¥çŽæå®åãé©çšã§ããªã
virtual int compute(int x) pre(x > 0) = 0;
};
ããã©ã«ãåã»åé€ããã颿°
æåã®å®£èšã§ããã©ã«ãåïŒ= defaultïŒããã颿°ã«äºåæ¡ä»¶ãŸãã¯äºåŸæ¡ä»¶æå®åãä»ããããšã¯äžé©æ ŒïŒill-formedïŒã§ããã
struct X {
X() pre(true) = default; // ãšã©ãŒ: æåã®å®£èšã§defaultå
X(const X&) pre(true) = default; // ãšã©ãŒ: æåã®å®£èšã§defaultå
};
ãã ããæåã®å®£èšã§ã¯ãªã宣èšã§ããã©ã«ãåããå Žåã¯å¥çŽæå®åãä»ããããšãã§ããã
struct Y {
Y() pre(true); // æåã®å®£èšã«å¥çŽæå®å
};
Y::Y() pre(true) = default; // OK: æåã®å®£èšã§ã¯ãªãïŒpre(true)ã¯çç¥å¯èœïŒ
ãŸããæç€ºçã«åé€ïŒ= deleteïŒããã颿°ã«äºåæ¡ä»¶ãŸãã¯äºåŸæ¡ä»¶æå®åãä»ããããšã¯äžé©æ Œã§ããã
struct Z {
Z() pre(true) = delete; // ãšã©ãŒ: åé€ããã颿°
Z& operator=(const Z&) pre(true) = delete; // ãšã©ãŒ: åé€ããã颿°
};
ã³ã³ã¹ãã©ã¯ã¿ãšãã¹ãã©ã¯ã¿ã®å¶çŽ
ã³ã³ã¹ãã©ã¯ã¿ã®äºåæ¡ä»¶ã¢ãµãŒã·ã§ã³ãŸãã¯ãã¹ãã©ã¯ã¿ã®äºåŸæ¡ä»¶ã¢ãµãŒã·ã§ã³ã®è¿°èªå
ã§ãã¯ã©ã¹ã®ééçããŒã¿ã¡ã³ããthis->ãªãã§çŽæ¥åç
§ãããšãããã°ã©ã ã¯äžé©æ Œãšãªããããã¯ããªããžã§ã¯ãã®çåæéã®éå§åãŸãã¯çµäºåŸã«ã¡ã³ãã«ã¢ã¯ã»ã¹ããæªå®çŸ©åäœã®ãªã¹ã¯ãæå°éã«æããããã§ããã
struct X {
int i = 0;
bool f();
X()
pre(i == 0) // ãšã©ãŒ: thisãªãã§ã¡ã³ãåç
§
pre(f()) // ãšã©ãŒ: thisãªãã§ã¡ã³ã颿°åŒã³åºã
pre(this->i == 0) // OK: thisãæç€ºçã«äœ¿çš
pre(this->f()) // OK
{}
~X()
pre(i == 0) // OK: ãã¹ãã©ã¯ã¿ã®äºåæ¡ä»¶
post(i == 0) // ãšã©ãŒ: ãã¹ãã©ã¯ã¿ã®äºåŸæ¡ä»¶ã§ã¡ã³ãåç
§
post(this->i == 0) // OK
{}
};
awaitåŒãšyieldåŒ
ã³ã«ãŒãã³å
ã®å¥çŽã¢ãµãŒã·ã§ã³ã®è¿°èªã«ããã®ã³ã«ãŒãã³ã®äžæã³ã³ããã¹ãå
ã«ããawaitåŒãŸãã¯yieldåŒãå«ãŸããŠããå Žåãããã°ã©ã ã¯äžé©æ Œãšãªãã
std::generator<int> f() {
contract_assert(((co_yield 1), true)); // ãšã©ãŒ
}
stdex::task<void> g() {
contract_assert((co_await query_database()) > 0); // ãšã©ãŒ
}
颿°ãžã®ãã€ã³ã¿ãšã¡ã³ã颿°ãžã®ãã€ã³ã¿
颿°ãžã®ãã€ã³ã¿ãã¡ã³ã颿°ãžã®ãã€ã³ã¿ã«å¥çŽæå®åãé©çšããããšã¯ã§ããªãã
typedef int (*fpt)(int) post(r: r != 0); // ãšã©ãŒ
int f(int x) post(r: r != 0);
int (*fp)(int) post(r: r != 0) = f; // ãšã©ãŒ
ãã ããå¥çŽã¢ãµãŒã·ã§ã³ã¯é¢æ°ã®åã®äžéšã§ã¯ãªããããå¥çŽä»ãã®é¢æ°ã®ã¢ãã¬ã¹ãéåžžã®é¢æ°ãã€ã³ã¿ã«ä»£å ¥ã§ããã
int f(int x) post(r: r != 0);
int (*fp)(int) = f; // OK
颿°ãã€ã³ã¿ãéããŠé¢æ°ãåŒã³åºãå Žåã§ãããã®é¢æ°ã®å¥çŽã¢ãµãŒã·ã§ã³ã¯éåžžéãè©äŸ¡ãããå¿ èŠãããã
颿°åãšã€ãªã¢ã¹
颿°åãšã€ãªã¢ã¹ã«å¥çŽæå®åãé©çšããããšã¯ã§ããªãã
using ft = int(int) post(r: r != 0); // ãšã©ãŒ
ãã ãã颿°åãšã€ãªã¢ã¹ã䜿çšãã颿°å®£èšã«ã¯å¥çŽæå®åãé©çšã§ããã
using ft = int(int);
ft f post(r: r != 0); // OK
Cèšèªã®å¯å€é·åŒæ°ãã©ã¡ãŒã¿ã®äœ¿çš
å¥çŽè¿°èªå
ã§va_startãã¯ãã䜿çšãããšãããã°ã©ã ã¯äžé©æ ŒãšãªãïŒèšºæäžèŠïŒã
æå³è«
ååæ¢çŽ¢ãšã¢ã¯ã»ã¹å¶åŸ¡
颿°å¥çŽã¢ãµãŒã·ã§ã³ã®è¿°èªã«å¯Ÿããååæ¢çŽ¢ãšã¢ã¯ã»ã¹å¶åŸ¡ã®ã«ãŒã«ã¯ããã®é¢æ°ã®å®£èšã®ä»ã®éšåãšåæ§ã«é©çšãããã
- ééçã¡ã³ã颿°ã®å®£èšã®äžéšãšããŠã
thisåŒã䜿çšå¯èœã§ããã颿°ã®æé»ã®ãªããžã§ã¯ããã©ã¡ãŒã¿ãåç §ããã - ã¡ã³ã颿°ã®å Žåãè¿°èªã¯privateã¡ã³ãã«ã¢ã¯ã»ã¹ã§ããã
- äºåŸæ¡ä»¶ã¢ãµãŒã·ã§ã³ã¯çµæãã€ã³ãã£ã³ã°ïŒresult bindingïŒãšããæ°ãã宣èšããã®ã¹ã³ãŒãã«å°å ¥ãããã®ååã¯å€åŽã®ã¹ã³ãŒãã®ä»ã®ååãé ãã
struct Y {
int i;
public:
void f() pre(i == 0); // OK
friend void g(Y* y) pre(y->i == 0); // OK
};
int r = 10;
int h() post(r : r != ::r); // OK: çµæãã€ã³ãã£ã³ã°ã®rã¯å€åŽã®rãé ã
æé»ã®constæ§
å¥çŽãã§ãã¯ã¯ãããã°ã©ã ã®ç¶æ
ã倿Žããã®ã§ã¯ãªã芳å¯ãããã®ã§ããããããä¿é²ããããã«ãå¥çŽè¿°èªå
ã§åç
§ããã倿°ã«ã¯æé»çã«const修食ãé©çšãããã
void f(int x) {
contract_assert(++x > 0); // ãšã©ãŒ: xã¯æé»çã«constãšããŠæ±ããã
}
ãã ããè¿°èªå
ã§åŒã³åºããã颿°ã¯constexprã§ããå¿
èŠã¯ãªãããã®ã³ã°ãªã©ã®ãããã°ç®çã§å¯äœçšãæã€é¢æ°ãåŒã³åºãããšã¯èš±å¯ãããããããã°ã©ã ã®æ£åœæ§ã«åœ±é¿ãäžããç Žå£çãªå¯äœçšã¯é¿ããã¹ãã§ããã
çµæãã€ã³ãã£ã³ã°
äºåŸæ¡ä»¶ã®çµæãã€ã³ãã£ã³ã°ã¯ã颿°ã®æ»ãå€ãªããžã§ã¯ããåç
§ãããçµæãã€ã³ãã£ã³ã°ã«ãã£ãŠå°å
¥ããã倿°ã¯ã宿°ïŒconstïŒãªå·ŠèŸºå€åç
§ã§ããã
int f(int i)
post(result: result >= i); // resultã¯æ»ãå€ãžã®å®æ°åç
§
çµæãã€ã³ãã£ã³ã°ã«ã¯ã[[maybe_unused]]ãªã©ã®å±æ§ãé©çšã§ããã
int g()
post(r [[maybe_unused]]: true); // OK
äºåŸæ¡ä»¶ã«ããã颿°ãã©ã¡ãŒã¿
äºåŸæ¡ä»¶ã¢ãµãŒã·ã§ã³ã®è¿°èªå ã§é¢æ°ãã©ã¡ãŒã¿ã䜿çšã§ããããã ããé åãã©ã¡ãŒã¿ãODR䜿çšïŒOne Definition Rule useïŒããããšã¯äžé©æ Œã§ããã
int sum(int arr[], int n)
post(r: r >= 0); // OK: é
åãã©ã¡ãŒã¿ã䜿çšããŠããªã
int process(int arr[], int n)
post(r: arr[0] > 0); // ãšã©ãŒ: é
åãã©ã¡ãŒã¿ãODR䜿çš
è©äŸ¡ã®é çª
å¥çŽã¢ãµãŒã·ã§ã³ã®è©äŸ¡é åºã¯ä»¥äžã®éãã§ããã
è©äŸ¡ã®ã¿ã€ãã³ã°
- äºåæ¡ä»¶ã¢ãµãŒã·ã§ã³: 颿°ãã©ã¡ãŒã¿ã®åæååŸã颿°æ¬äœã®è©äŸ¡éå§åã«è©äŸ¡ãããã
- äºåŸæ¡ä»¶ã¢ãµãŒã·ã§ã³: æ»ãå€ã®åæååŸãreturnæã«ãã£ãŠæããã¹ã³ãŒãã®ããŒã«ã«å€æ°ã®ç Žæ£åŸããã ã颿°ãã©ã¡ãŒã¿ã®ç Žæ£åã«è©äŸ¡ãããã
- ã¢ãµãŒã·ã§ã³æ: å¶åŸ¡ãããŒããã®æã«å°éããæç¹ã§å®è¡ãããã
ã³ã³ã¹ãã©ã¯ã¿ãšãã¹ãã©ã¯ã¿ã§ã®è©äŸ¡ã¿ã€ãã³ã°ã®è©³çްã«ã€ããŠã¯ããã³ã³ã¹ãã©ã¯ã¿ãšãã¹ãã©ã¯ã¿ã®å¶çŽãããã³ãã¬ãžã¹ã¿ã§æž¡ãããã»è¿ããããªããžã§ã¯ãããåç §ã®ããšã
è€æ°ã®å¥çŽæå®åã®è©äŸ¡é åº
颿°å¥çŽã¢ãµãŒã·ã§ã³ã®ã·ãŒã±ã³ã¹å ã«è€æ°ã®äºåæ¡ä»¶ãŸãã¯äºåŸæ¡ä»¶ã¢ãµãŒã·ã§ã³ãããå Žåããããã¯å®£èšãããé åºã§è©äŸ¡ãããã
void f()
pre(a())
pre(b())
post(c())
post(d())
{
// äºåæ¡ä»¶ã®è©äŸ¡é : a(), b()
// äºåŸæ¡ä»¶ã®è©äŸ¡é : c(), d()
}
æ»ãå€ã®ã³ããŒãšã·ãŒã±ã³ã¹
颿°ã®æ»ãå€ã®åãã¬ãžã¹ã¿ã§æž¡ãããè³æ Œãããå Žåãã³ã³ãã€ã©ã¯æ»ãå€ãªããžã§ã¯ãã®è¿œå ã®ããªãã¢ã«ãªã³ããŒãäœæããããšãèš±å¯ãããŠãããäºåŸæ¡ä»¶ã¢ãµãŒã·ã§ã³ã¯ãããã®ã³ããŒãåç §ããå¯èœæ§ãããããã ãããããã®ã³ããŒã¯äºåŸæ¡ä»¶ã¢ãµãŒã·ã§ã³ã®è©äŸ¡ãšé åºä»ããããŠè¡ãããªããã°ãªããªãã
ãããã£ãŠããã¹ãŠã®å¥çŽã¢ãµãŒã·ã§ã³ããã§ãã¯ã»ãã³ãã£ã¯ã¹ã§æ£ç¢ºã«1åè©äŸ¡ãããããã«ããã°ã©ã ããã«ããããŠããå Žåã以äžã®äŸã§ã¯ãrãåããªããžã§ã¯ããåç
§ãããã©ããã«é¢ããããäž¡æ¹ã®äºåŸæ¡ä»¶ã¢ãµãŒã·ã§ã³ã¯trueãšè©äŸ¡ãããªããã°ãªããªãã
int f()
post(r: ++const_cast<int&>(r) == 1)
post(r: ++const_cast<int&>(r) == 2)
{
return 0;
}
è¿°èªã®è©äŸ¡
å¥çŽã¢ãµãŒã·ã§ã³ã®è¿°èªåŒãè©äŸ¡ããããšããããã¯boolã«æèçã«å€æããããè¿°èªåŒã¯å®å
šåŒã§ãããããè¿°èªã®è©äŸ¡äžã«äœæãããäžæãªããžã§ã¯ãã¯ããã®è©äŸ¡ãå®äºãããšãã«ç Žæ£ãããã
å¥çŽã®è©äŸ¡ã¢ãŒã
åå¥çŽã¢ãµãŒã·ã§ã³ã®åå¥ã®è©äŸ¡ã¯ãç¹å®ã®è©äŸ¡ã»ãã³ãã£ã¯ã¹ã§å®è¡ããããææ¡ãããŠãã4ã€ã®è©äŸ¡ã»ãã³ãã£ã¯ã¹ã¯ä»¥äžã®éãã§ããã
ignoreïŒç¡èŠïŒ
ignoreã»ãã³ãã£ã¯ã¹ã¯äœãè¡ããªããå¥çŽè¿°èªã¯è©äŸ¡ããããå¯äœçšãçºçããªããããã¯éãã§ãã¯ã»ãã³ãã£ã¯ã¹ã§ããã
observeïŒèгå¯ïŒ
observeã»ãã³ãã£ã¯ã¹ã¯è¿°èªãè©äŸ¡ããŠå¥çŽéåãèå¥ãããå¥çŽéåãçºçããå Žåãå¥çŽéåãã³ãã©ãåŒã³åºãããã³ãã©ãæ£åžžã«æ»ããšãããã°ã©ã ã®å®è¡ã¯ç¶è¡ããããããã¯ãã§ãã¯ã»ãã³ãã£ã¯ã¹ã§ããã
enforceïŒåŒ·å¶ïŒ
enforceã»ãã³ãã£ã¯ã¹ã¯è¿°èªãè©äŸ¡ããŠå¥çŽéåãèå¥ãããå¥çŽéåãçºçããå Žåãå¥çŽéåãã³ãã©ãåŒã³åºãããã³ãã©ãæ£åžžã«æ»ããšãããã°ã©ã ã¯å®è£
å®çŸ©ã®æ¹æ³ã§çµäºããïŒéåžžã¯std::terminate()ãåŒã³åºãããé¡äŒŒã®æ¹æ³ïŒãããã¯ãã§ãã¯ã»ãã³ãã£ã¯ã¹ã§ãããçµäºã»ãã³ãã£ã¯ã¹ã§ãããã
quick-enforceïŒé«é匷å¶ïŒ
quick-enforceã»ãã³ãã£ã¯ã¹ã¯è¿°èªãè©äŸ¡ããŠå¥çŽéåãèå¥ãããå¥çŽéåãçºçããå Žåãå¥çŽéåãã³ãã©ãåŒã³åºããã«ãå³åº§ã«ããã°ã©ã ãå®è£
å®çŸ©ã®æ¹æ³ã§çµäºãããããã¯ãã§ãã¯ã»ãã³ãã£ã¯ã¹ã§ãããçµäºã»ãã³ãã£ã¯ã¹ã§ããããquick-enforceã¯ãå¥çŽéåãã³ãã©ã®ãªãŒããŒããããé¿ãããããããã©ãŒãã³ã¹ãéèŠãªç¶æ³ã§æçšã§ããã
è©äŸ¡ã»ãã³ãã£ã¯ã¹ã®éžæ
å¥çŽã¢ãµãŒã·ã§ã³ã®åå¥ã®è©äŸ¡ã«å¯ŸããŠã©ã®è©äŸ¡ã»ãã³ãã£ã¯ã¹ã䜿çšãããããéžæããã¡ã«ããºã ã¯å®è£ å®çŸ©ã§ãããã³ã³ãã€ã«æãªãã·ã§ã³ãå®è¡æèšå®ããŸãã¯ãã®çµã¿åããã§æå®ã§ããã
åŠçç³»ããšã®æå®æ¹æ³ã¯ãã³ã³ãã€ã«ãªãã·ã§ã³ãåç §ã
å¥çŽè¿°èªã®ãã§ãã¯
ãã§ãã¯ã»ãã³ãã£ã¯ã¹ïŒobserveãenforceãquick-enforceïŒã§ã¯ãè¿°èªãè©äŸ¡ãããboolã«æè倿ããããè¿°èªãtrueã«è©äŸ¡ããããšãå¥çŽéåã¯èå¥ãããªããè¿°èªãfalseã«è©äŸ¡ãããããè¿°èªã®è©äŸ¡ãäŸå€ã§çµäºãããšãå¥çŽéåãèå¥ãããã
è¿°èªã®è©äŸ¡ãã¹ã¿ãã¯ãéããŠå¶åŸ¡ãè¿ããªãä»ã®çµæïŒçµäºãç¡éã«ãŒããlongjmpã®åŒã³åºããªã©ïŒãçºçããå Žåãä»ã®C++åŒã®è©äŸ¡æãšåæ§ã«åŠçãããã
çç¥ãéè€ãé æ¬¡è©äŸ¡
å®è£ ã¯ãå¥çŽã¢ãµãŒã·ã§ã³ã®è©äŸ¡ãçç¥ãéè€ããŸãã¯ç¹å®ã®é åºã§å®è¡ã§ããããã ããå¯äœçšãæ£åžžã«æ»ãå Žåã«ã®ã¿çç¥ã§ããããã®æè»æ§ã«ãããå®è£ ã¯æé©åã®æ©äŒãæã€ã
è¿°èªã®å¯äœçš
å¥çŽè¿°èªã«å¯äœçšãããããšã¯æšå¥šãããªãããå®å šã«çŠæ¢ãããŠããããã§ã¯ãªããè¿°èªã«ç Žå£çãªå¯äœçšïŒããã°ã©ã ã®æ£åœæ§ã«åœ±é¿ãäžããå¯äœçšïŒãããå Žåããã®åäœã¯èšèšååã«éåããããã ãããã®ã³ã°ããããã°ç®çã®éç Žå£çãªå¯äœçšã¯èš±å¯ãããã
芳å¯å¯èœãªãã§ãã¯ãã€ã³ã
å¥çŽã¢ãµãŒã·ã§ã³ã®è©äŸ¡ã¯ã芳å¯å¯èœãªãã§ãã¯ãã€ã³ãã§ãããããã¯ãã³ã³ãã€ã©ãå¥çŽã¢ãµãŒã·ã§ã³ã®è©äŸ¡ã®ååŸã§ã¡ã¢ãªã¢ã¯ã»ã¹ã®äžŠã¹æ¿ããè¡ããªãããšãæå³ããã
ã³ã«ãŒãã³ã®ãµããŒã
ã³ã«ãŒãã³ã«ã¯å¥çŽæå®åãé©çšã§ãããäºåæ¡ä»¶ã¢ãµãŒã·ã§ã³ã¯ã³ã«ãŒãã³ã®åŒæ°ãåæåãããåŸã«è©äŸ¡ãããäºåŸæ¡ä»¶ã¢ãµãŒã·ã§ã³ã¯ã³ã«ãŒãã³ãco_returnã§çµäºãããšãã«è©äŸ¡ãããã
宿°è©äŸ¡
å¥çŽã¢ãµãŒã·ã§ã³ã¯å®æ°è©äŸ¡äžã«ãè©äŸ¡ã§ããã宿°è©äŸ¡äžãå¥çŽéåãã³ãã©ã¯åŒã³åºãããªãã代ããã«ããã§ãã¯ã»ãã³ãã£ã¯ã¹ã§å¥çŽéåãèå¥ããããšã宿°è©äŸ¡ã¯å€±æããã
å¥çŽéåãã³ãã©
ãã³ãã©ã®å®çŸ©
å¥çŽéåãã³ãã©ã¯ããŠãŒã¶ãŒããªã³ã¯æã«ã«ã¹ã¿ã å®çŸ©ã§ãã颿°ã§ããããã³ãã©ã®çœ²åã¯ä»¥äžã®éãïŒ
void handle_contract_violation(const std::contracts::contract_violation& violation);
ãã®é¢æ°ãå®çŸ©ããããšã§ãããã©ã«ãã®å¥çŽéåãã³ãã©ããªãŒããŒã©ã€ãã§ããã
ãã ããå¥çŽéåãã³ãã©ã眮ãæãããããã©ããã¯åŠçç³»å®çŸ©ã§ããã眮ãæããããªãåŠçç³»ã§ãã®é¢æ°ã宣èšããå Žåããã®ããã°ã©ã ã¯äžé©æ ŒãšãªãïŒèšºæäžèŠïŒã眮ãæãããããã©ããã¯ã<contracts>ãããã§å®çŸ©ãããæ©èœãã¹ããã¯ã__cpp_lib_replaceable_contract_violation_handlerã«ãã£ãŠå€å®ã§ããããã®ãã¯ãã¯ã眮ãæããããå Žåã«202603Lã眮ãæããããªãå Žåã«0ãšãªãã
#include <contracts>
#if __cpp_lib_replaceable_contract_violation_handler
void handle_contract_violation(const std::contracts::contract_violation& violation) {
// ...
}
#endif
contract_violationæ å ±
std::contracts::contract_violationãªããžã§ã¯ãã¯ãå¥çŽéåã«é¢ãã以äžã®æ
å ±ãæäŸããïŒ
- éåã®çš®é¡ (
assertion_kind): äºåæ¡ä»¶ãäºåŸæ¡ä»¶ããŸãã¯ã¢ãµãŒã·ã§ã³æã®ãããã - è©äŸ¡ã»ãã³ãã£ã¯ã¹ (
evaluation_semantic): éåãèå¥ããè©äŸ¡ã»ãã³ãã£ã¯ã¹ - æ€åºã¢ãŒã (
detection_mode): å¥çŽéåãã©ã®ããã«æ€åºããããïŒè¿°èªãfalseã«è©äŸ¡ãããããäŸå€ãçºçãããïŒ - ãœãŒã¹ãã±ãŒã·ã§ã³ (
location): éåããå¥çŽã¢ãµãŒã·ã§ã³ã®ãœãŒã¹ãã¡ã€ã«åãè¡çªå·ã颿°å - çµäºå€å® (
is_terminating): ãã®éååŸã«ããã°ã©ã ãçµäºãããã©ãã
ãã³ãã©ã®åäœ
å¥çŽéåãèå¥ããããšãobserveãŸãã¯enforceã»ãã³ãã£ã¯ã¹ã®å Žåãå¥çŽéåãã³ãã©ãåŒã³åºãããïŒ
- observeã»ãã³ãã£ã¯ã¹: ãã³ãã©ãæ£åžžã«æ»ããšãããã°ã©ã ã®å®è¡ã¯ç¶è¡ãããã
- enforceã»ãã³ãã£ã¯ã¹: ãã³ãã©ãæ£åžžã«æ»ããšãããã°ã©ã ã¯å®è£ å®çŸ©ã®æ¹æ³ã§çµäºããã
- quick-enforceã»ãã³ãã£ã¯ã¹: ãã³ãã©ã¯åŒã³åºããããå³åº§ã«ããã°ã©ã ãçµäºããã
ããã©ã«ããã³ãã©
ãŠãŒã¶ãŒãã«ã¹ã¿ã ãã³ãã©ãå®çŸ©ããªãå Žåãå®è£ ãæäŸããããã©ã«ããã³ãã©ã䜿çšããããããã©ã«ããã³ãã©ã¯ãæšæºãšã©ãŒåºåã«èšºæã¡ãã»ãŒãžãåºåããããšãæåŸ ãããã
ãã³ãã©å ã§ã®å¥çŽéå
å¥çŽéåãã³ãã©ã®å®è¡äžã«å¥ã®å¥çŽéåãçºçããå ŽåïŒååž°çå¥çŽéåïŒãåäœã¯å®è£ å®çŸ©ã§ãããéåžžã¯ãç¡éååž°ãé¿ããããã«å³åº§ã«ããã°ã©ã ãçµäºããã
äŸå€ãæãããã³ãã©
å¥çŽéåãã³ãã©ããäŸå€ãæããããšã¯èš±å¯ãããŠããããæšå¥šãããªãããã³ãã©ãäŸå€ã§çµäºããå Žåããã®äŸå€ã¯é¢æ°æ¬äœ (contract_assertã®å Žåã¯ãã®æ) ããéåºããããã®ããã«äŒæãããäŸå€ãnoexcept颿°ã®å€ãžåºãããšããå Žåã¯ãstd::terminate()ãåŒã³åºãããã
æšæºã©ã€ãã©ãªAPI
<contracts>ããããŒ
C++26ã§ã¯ãå¥çŽããã°ã©ãã³ã°ããµããŒãããããã«<contracts>ããããŒãå°å
¥ãããããã®ããããŒã«ã¯ä»¥äžã®åãšé¢æ°ãå«ãŸããã
åæå
assertion_kind
å¥çŽã¢ãµãŒã·ã§ã³ã®çš®é¡ã衚ãåæåïŒ
namespace std::contracts {
enum class assertion_kind {
pre, // äºåæ¡ä»¶ã¢ãµãŒã·ã§ã³
post, // äºåŸæ¡ä»¶ã¢ãµãŒã·ã§ã³
assert // ã¢ãµãŒã·ã§ã³æ
};
}
evaluation_semantic
å¥çŽã¢ãµãŒã·ã§ã³ã®è©äŸ¡ã»ãã³ãã£ã¯ã¹ã衚ãåæåïŒ
namespace std::contracts {
enum class evaluation_semantic {
ignore, // ç¡èŠ
observe, // 芳å¯
enforce, // 匷å¶
quick_enforce // é«é匷å¶
};
}
detection_mode
å¥çŽéåãã©ã®ããã«æ€åºããããã衚ãåæåïŒ
namespace std::contracts {
enum class detection_mode {
predicate_false, // è¿°èªãfalseã«è©äŸ¡ããã
evaluation_exception // è¿°èªã®è©äŸ¡äžã«äŸå€ãçºç
};
}
contract_violationã¯ã©ã¹
å¥çŽéåã«é¢ããæ å ±ãæäŸããã¯ã©ã¹ïŒ
namespace std::contracts {
class contract_violation {
public:
assertion_kind kind() const noexcept;
evaluation_semantic semantic() const noexcept;
contracts::detection_mode detection_mode() const noexcept;
source_location location() const noexcept;
const char* comment() const noexcept;
bool is_terminating() const noexcept;
exception_ptr evaluation_exception() const noexcept;
};
}
äž»ãªã¡ã³ã颿°ïŒ
kind(): éåããå¥çŽã¢ãµãŒã·ã§ã³ã®çš®é¡ãè¿ãsemantic(): 䜿çšãããè©äŸ¡ã»ãã³ãã£ã¯ã¹ãè¿ãdetection_mode(): éåã®æ€åºæ¹æ³ãè¿ãlocation(): å¥çŽã¢ãµãŒã·ã§ã³ã®ãœãŒã¹ãã±ãŒã·ã§ã³ãè¿ãcomment(): ãã³ããŒåºæã®ã³ã¡ã³ãæååãè¿ãis_terminating(): éååŸã«ããã°ã©ã ãçµäºãããã©ãããè¿ãevaluation_exception(): è¿°èªã®è©äŸ¡äžã«äŸå€ãçºçããå Žåããã®äŸå€ãžã®exception_ptrãè¿ã
invoke_default_contract_violation_handler颿°
ããã©ã«ãã®å¥çŽéåãã³ãã©ãæç€ºçã«åŒã³åºãããã®é¢æ°ïŒ
namespace std::contracts {
void invoke_default_contract_violation_handler(
const contract_violation& violation);
}
ãã®é¢æ°ã¯ãã«ã¹ã¿ã ãã³ãã©å ã§ããã©ã«ãã®åäœãåŒã³åºãããå Žåã«æçšã§ããã
䜿çšäžã®æ³šæ
以äžã®æäœã«ã¯æ³šæãå¿ èŠã§ããã
å¯äœçš
å¥çŽè¿°èªå
ã§å¯äœçšãæã€åŒãèšè¿°ããããšã¯å¯èœã ããæšå¥šãããªããç Žå£çãªå¯äœçšïŒã°ããŒãã«å€æ°ã®å€æŽãvolatile倿°ãžã®åç
§ãªã©ïŒã¯é¿ããã¹ãã§ããããã ãããã®ã³ã°ãªã©ã®éç Žå£çãªå¯äœçšã¯èš±å¯ãããã
// æšå¥šãããªãäŸ
int global_counter = 0;
void f(int x) pre(++global_counter > 0); // å¯äœçšãã
// 蚱容ãããäŸïŒãããã°ç®çïŒ
void g(int x) pre(log_value(x), x > 0); // ãã®ã³ã°ã¯èš±å®¹
äŸå€
å¥çŽè¿°èªå
ã§äŸå€ãéåºããããšãå¥çŽéåãšããŠæ±ããããobserveãŸãã¯enforceã»ãã³ãã£ã¯ã¹ã®å Žåãå¥çŽéåãã³ãã©ãåŒã³åºããããã®åŸã®åäœã¯ã»ãã³ãã£ã¯ã¹ã«äŸåããã
bool might_throw(int x);
void f(int x) pre(might_throw(x)); // äŸå€çºçæã¯å¥çŽéå
ç¹æ®ã¡ã³ã颿°
= defaultãŸãã¯= deleteã§å®çŸ©ãããç¹æ®ã¡ã³ã颿°ã«å¥çŽæå®åãé©çšãããšãããã°ã©ã ã¯äžé©æ Œãšãªããéåžžã®ã³ã³ã¹ãã©ã¯ã¿ããã¹ãã©ã¯ã¿ãããã³ã¡ã³ã颿°ã«ã¯å¥çŽãé©çšã§ããã
assert ãã¯ããšã®éã
å¥çŽããã°ã©ãã³ã°æ©èœãšåŸæ¥ã®assertãã¯ãã«ã¯ä»¥äžã®éããããïŒ
- ããŒã¯ãŒã vs ãã¯ã:
contract_assertã¯ããŒã¯ãŒãã§ãããassertã¯ãã¯ãã§ãã - è©äŸ¡å¶åŸ¡: å¥çŽã®è©äŸ¡ã»ãã³ãã£ã¯ã¹ã¯å®è£
å®çŸ©ã®æ¹æ³ã§éžæããããã
assertã¯NDEBUGãã¯ãã®æç¡ã§å¶åŸ¡ããã - äºåŸæ¡ä»¶: å¥çŽããã°ã©ãã³ã°ã¯äºåŸæ¡ä»¶ãç°¡æœã«èšè¿°ã§ãããã
assertãã¯ãã§ã¯å°é£ - æšæºå: å¥çŽããã°ã©ãã³ã°ã¯C++æšæºã®äžéšã§ããã
assertã¯Cããç¶æ¿ããããã¯ãã§ãã
ã³ã³ãã€ã«ãªãã·ã§ã³
å¥çŽã¢ãµãŒã·ã§ã³ãã©ã®è©äŸ¡ã»ãã³ãã£ã¯ã¹ã§è©äŸ¡ãããã¯å®è£ å®çŸ©ã§ãããåŠçç³»ãæäŸããã³ã³ãã€ã«ãªãã·ã§ã³ã§æå®ããã
GCC
| ãªãã·ã§ã³ | 説æ |
|---|---|
-fcontracts |
å¥çŽããã°ã©ãã³ã°æ©èœãæå¹ã«ããã-std=c++26ãæå®ããå Žåã¯æé»ã«æå¹ãšãªã |
-fcontract-evaluation-semantic= |
è©äŸ¡ã»ãã³ãã£ã¯ã¹ãignoreãobserveãenforceãquick_enforceã®ããããã«æå®ãããããã©ã«ãã¯enforce |
-fcontracts-client-check= |
åŒã³åºãåŽã§ãã§ãã¯ããã¢ãµãŒã·ã§ã³ãnoneãpreãallã®ããããã«æå®ãããããã©ã«ãã¯none |
-fcontracts-definition-check= |
å®çŸ©åŽã§ã®ãã§ãã¯ãonãoffã®ããããã«æå®ãããããã©ã«ãã¯on |
-fcontract-checks-outlined |
äºåæ¡ä»¶ã»äºåŸæ¡ä»¶ã®ãã§ãã¯ã颿°æ¬äœã«å±éãããå¥ã®é¢æ°ãšããŠåãåºããŠåŒã³åºã |
-fcontract-disable-optimized-checks |
-fcontract-checks-outlinedã§åãåºãã颿°ã«å¯Ÿããæé©åãæå¶ãã |
-fcontracts-conservative-ipa |
翻蚳åäœããšã«è©äŸ¡ã»ãã³ãã£ã¯ã¹ãç°ãªãããããšãåæãšããŠãã€ã³ã©ã€ã³é¢æ°ã«å¯Ÿããæç¶ãéè§£æãæå¶ãããããã©ã«ãã§æå¹ |
# å¥çŽã¢ãµãŒã·ã§ã³ãobserveã»ãã³ãã£ã¯ã¹ã§è©äŸ¡ãã
g++ -std=c++26 -fcontracts -fcontract-evaluation-semantic=observe main.cpp
- è©äŸ¡ã»ãã³ãã£ã¯ã¹ã¯ãã³ã³ãã€ã«æã«ç¿»èš³åäœããšã«æ±ºå®ããããå¥çŽã¢ãµãŒã·ã§ã³ããšã«æå®ããæ¹æ³ããå®è¡æã«åãæ¿ããæ¹æ³ã¯æäŸãããŠããªã
- 翻蚳åäœããšã«ç°ãªãã»ãã³ãã£ã¯ã¹ãæå®ããå Žåã颿°ãå®çŸ©ãã翻蚳åäœã®ã»ãã³ãã£ã¯ã¹ãé©çšããã
- å¥çŽéåãã³ãã©ã®ããã©ã«ãã®å®è£
ã¯
libstdc++expã©ã€ãã©ãªãæäŸããããã®ã©ã€ãã©ãªã¯ãã³ãã³ãã©ã€ã³ã«-fcontractsãæå®ããå Žåã«èªåçã«ãªã³ã¯ãããã-std=c++26ã«ãã£ãŠæé»ã«æå¹åããå Žåã¯ãªã³ã¯ãããªãããã-lstdc++expãªãã·ã§ã³ãæå®ããå¿ èŠããã - GCC 15以åã®
-fcontractsã¯ãC++26ã«æ¡çšãããP2900ãšã¯ç°ãªã仿§ (N4820ãP1332ãP1429) ã®å®éšçãªå®è£ ã§ããã-fcontract-build-levelã-fcontract-roleãªã©ã®å¥ã®ãªãã·ã§ã³ãæäŸããŠãã
äŸ
åºæ¬çãªäœ¿çšäŸ
#include <contracts>
#include <iostream>
// äºåæ¡ä»¶ãšäºåŸæ¡ä»¶ãæã€é¢æ°
int safe_division(const int numerator, const int denominator)
pre(denominator != 0)
post(result: result * denominator == numerator)
{
return numerator / denominator;
}
// è€æ°ã®å¥çŽæ¡ä»¶
class BankAccount {
double balance = 0.0;
public:
void deposit(double amount)
pre(amount > 0)
post(balance >= old_balance) // 泚: oldå€ã®åç
§ã¯C++26ã§ã¯æªãµããŒã
{
balance += amount;
}
void withdraw(double amount)
pre(amount > 0)
pre(amount <= balance) // è€æ°ã®äºåæ¡ä»¶
post(balance >= 0)
{
contract_assert(balance >= amount); // ã¢ãµãŒã·ã§ã³æ
balance -= amount;
}
double get_balance() const
post(result: result >= 0)
{
return balance;
}
};
// ã©ã ãåŒã§ã®äœ¿çš
auto lambda_with_contract = [](const int x)
pre(x > 0)
post(r: r > x)
{
return x + 1;
};
int main() {
// æ£åžžãªäœ¿çš
int result = safe_division(10, 2); // OK: result = 5
std::cout << result << '\n';
BankAccount account;
account.deposit(100.0); // OK
account.withdraw(50.0); // OK
// å¥çŽéåã®äŸïŒå®è¡æã«æ€åºãããïŒ
// safe_division(10, 0); // äºåæ¡ä»¶éå
// account.withdraw(200.0); // äºåæ¡ä»¶éå
auto value = lambda_with_contract(5); // OK: value = 6
return 0;
}
ã«ã¹ã¿ã å¥çŽéåãã³ãã©ã®äŸ
#include <contracts>
#include <iostream>
#include <cstdlib>
// ã«ã¹ã¿ã å¥çŽéåãã³ãã©ã®å®çŸ©
void handle_contract_violation(const std::contracts::contract_violation& v) {
std::cerr << "å¥çŽéåãçºçããŸãã:\n";
std::cerr << " çš®é¡: ";
switch (v.kind()) {
case std::contracts::assertion_kind::pre:
std::cerr << "äºåæ¡ä»¶\n";
break;
case std::contracts::assertion_kind::post:
std::cerr << "äºåŸæ¡ä»¶\n";
break;
case std::contracts::assertion_kind::assert:
std::cerr << "ã¢ãµãŒã·ã§ã³\n";
break;
}
std::cerr << " å Žæ: " << v.location().file_name()
<< ":" << v.location().line() << '\n';
std::cerr << " 颿°: " << v.location().function_name() << '\n';
if (v.is_terminating()) {
std::cerr << "ããã°ã©ã ãçµäºããŸãã\n";
}
// ããã©ã«ããã³ãã©ãåŒã³åºã
std::contracts::invoke_default_contract_violation_handler(v);
}
int process(const int x)
pre(x > 0)
post(r: r > x)
{
return x + 1;
}
int main() {
// å¥çŽéåãçºçãããšãã«ã¹ã¿ã ãã³ãã©ãåŒã³åºããã
// process(-1); // äºåæ¡ä»¶éå
return 0;
}
ãã³ãã¬ãŒã颿°ã§ã®äœ¿çšäŸ
#include <contracts>
#include <concepts>
#include <vector>
#include <limits>
template<std::integral T>
T increment(const T value)
pre(value < std::numeric_limits<T>::max())
post(result: result == value + 1)
{
return value + 1;
}
template<typename T>
class Stack {
std::vector<T> data;
public:
void push(const T& item)
post(size() == old_size + 1) // 泚: oldå€ã®åç
§ã¯C++26ã§ã¯æªãµããŒã
{
data.push_back(item);
}
T pop()
pre(!empty())
post(size() == old_size - 1) // 泚: oldå€ã®åç
§ã¯C++26ã§ã¯æªãµããŒã
{
T value = data.back();
data.pop_back();
return value;
}
bool empty() const
post(result: result == (size() == 0))
{
return data.empty();
}
std::size_t size() const
post(result: result <= data.capacity())
{
return data.size();
}
};
int main() {
auto value = increment(42); // OK
Stack<int> stack;
stack.push(10);
stack.push(20);
int top = stack.pop(); // OK: top = 20
return 0;
}
é¢é£é ç®
<contracts>ããããŒstd::contracts::contract_violationstd::contracts::assertion_kindstd::contracts::evaluation_semanticstd::contracts::detection_mode- C++29 ä»®æ³é¢æ°ãžã®äºåæ¡ä»¶ã»äºåŸæ¡ä»¶ã®æå®ãèš±å¯
åç §
- P2900R14
Contracts for C++ - P3886R0 Wording for AT1-057
- C++26ã§ãå¥çŽéåãã³ãã©ã眮ãæããããããæ€åºããæ©èœãã¹ããã¯ã
__cpp_lib_replaceable_contract_violation_handlerã远å ããã
- C++26ã§ãå¥çŽéåãã³ãã©ã眮ãæããããããæ€åºããæ©èœãã¹ããã¯ã