C ++ 17 срещу C ++ 14 - ако-constexpr

Радваме се да видим, че ако-constexpr го направи на C ++ 17. Можете да го опитате сами, като използвате текущия багажник на кланг.

В тази публикация в блога ние преразглеждаме някои C ++ 14 код и се опитваме да използваме новата функция.

Без да разполагате с if-constexpr на ваше разположение, често се налага да прибягвате до усъвършенствани техники за метапрограмиране, използвайки съответствие на шаблона, разделителна способност и SFINAE.

Пример 1 - получаване на nth-arg

Много метапрограми на шаблони работят в списъци с различни варианти. В C ++ 14 получаването на n-ти тип списъци с аргументи често се реализира по следния начин:

шаблон <неподписан n>
struct Arg {
 шаблон <клас X, клас ... Xs>
 constexpr auto operator () (X x, Xs… xs) {
   върнете Arg  {} (xs…);
 }
};
шаблон <>
struct Arg <0> {
 шаблон <клас X, клас ... Xs>
 constexpr auto operator () (X x, Xs…) {
   връщане x;
 }
};
шаблон <неподписан n>
constexpr auto arg = Arg  {};
// arg <2> (0,1,2,3,4,5) == 2;

C ++ 17 прави това малко по-интуитивно:

шаблон <неподписан n>
структура Получете {
 шаблон <клас X, клас ... Xs>
 constexpr auto operator () (X x, Xs… xs) {
   ако constexpr (n> sizeof… (xs)) {
     се върне;
   } else, ако constexpr (n> 0) {
     връщане Получете  {} (xs…);
   } else {
     връщане x;
   }
 }
};

Пример 2 - API - shimming

Понякога искате да поддържате алтернативен API. C ++ 14 предоставя лесен начин да проверите дали даден обект може да се използва по определен начин:

шаблон <клас T>
constexpr автоматично поддържаAPI (T x) -> decltype (x.Method1 (), x.Method2 (), true_type {}) {
 връщане {};
}
constexpr автоматично поддържаAPI (…) -> false_type {
 връщане {};
}

Внедряването на персонализирано поведение в C ++ 14 може да се направи така:

шаблон <клас T>
автоматично изчисляване (T x) -> decltype (enable_if_t  {}) {
 връщане x.Method ();
}
шаблон <клас T>
автоматично изчисляване (T x) -> decltype (enable_if_t  {}) {
 връщане 0;
}

C ++ 17:

шаблон <клас T>
int изчисляване (T x) {
 ако constexpr (поддържаAPI (T {})) {
   // се компилира само ако условието е вярно
   връщане x.Method ();
 } else {
   връщане 0;
 }
}

Това е много удобно, тъй като кодът, който принадлежи семантично заедно, не е разпръснат в множество функции. Освен това можете дори да определите ламбдатите, съдържащи if-constexpr.

Пример 3 - Избор на алгоритъм за компилиране на време

Често трябва да намерите най-добрия алгоритъм въз основа на набор от правила и свойства на даден тип. Има много решения. Например, STL използва TypeTags, за да избере правилния алгоритъм за някои зададени итератори.

struct FooTag {};
struct BarTag {};
автоматично сгъване (...) {}
автоматично сгъванеFB (…) {}
автоматично сгъванеBF (…) {}
автоматично сгъванеBB (…) {}
Stru A {
 / *… * /
 използване на tag = FooTag;
};
структура B {
 / *… * /
 използване на tag = BarTag;
};
шаблон <клас L, клас R>
автоматично сгъване (L l, R r, FooTag, BarTag) {foldFB (l, r); }
/ * още диспечерски функции * /
шаблон <клас L, клас R>
автоматично сгъване (L l, R r) {
 възвратна сгъвка (l, r,
 typename L :: tag {},
 typename R :: tag {});
}

Въпреки това, след като имате по-сложни правила, може да се нуждаете от по-мощно решение - SFINAE:

C ++ 14:

struct BazTag: FooTag, BarTag {};
шаблон <клас L, клас R,
enable_if_t <
 is_same  :: стойност &&
 is_base_of  :: стойност
> сгъване (L l, R r) {
 връщане foldFB (l, r);
}

С C ++ 17 можете да опишете тези правила с по-малко котлони и по-ясно:

шаблон <клас L, клас R>
автоматично сгъване (L l, R r) {
 използване на lTag = typename L :: tag;
 използване на rTag = typename R :: tag;
ако constexpr (is_base_of  :: стойност) {
 ако constexpr (is_same  :: стойност) {
   връщане foldFB (l, r);
 } else {
   връщане на сгъванеBB (l, r);
 } else {
   връщане foldFF ();
 }
}

Това е много практично, тъй като работата с ifs е по-интуитивна, отколкото използването на различни езикови функции.

Рефакторинг мета-функции става толкова просто, колкото обикновения код. При if-constexpr притеснението от нееднозначни претоварвания и други неочаквани усложнения е нещо от миналото.

Ще надстроим нашия компилатор веднага щом Clang 3.9 е стабилен.