C++ Variadic шаблон И и ИЛИ

Можете ли да използвате различни шаблони на C++11, за да завършите /* ??? */ в:

template<bool...v> struct var_and { static bool constexpr value = /* ??? */; };

така че var_and<v...>::value предоставя && над булевия пакет v по време на компилация?

Можете ли да направите същото за struct var_or<v...> за ||?

Можете ли да използвате оценка на късо съединение (и в двата случая)?

Редактиране: Актуализация на приетия отговор добави, че C++17 сгъваеми изрази активират

template<bool... v> constexpr bool var_and = (v && ...);
template<bool... v> constexpr bool var_or  = (v || ...);

Изглежда, че за подходи, базирани на пакети с параметри, е възможен само ограничен тип „оценка на късо съединение“: докато инстанцирането на var_or<true,foo(),bar()> извиква само || веднъж, то също извиква и foo, и bar.


person nknight    schedule 09.08.2012    source източник
comment
Мирише на домашна работа.   -  person mah    schedule 10.08.2012
comment
@mah: Наистина се съмнявам, че има място, където да се преподава C++11 в момента.   -  person GManNickG    schedule 10.08.2012
comment
Това не е домашна работа. Пиша zip итератор (Boost's е недостатъчен); този „макрос“ е да определи дали всички итератори на компоненти имат определено общо свойство въз основа на булеви флагове. Сигурен съм, че има по-чист начин да го направите.   -  person nknight    schedule 10.08.2012
comment
В такъв случай изглежда добра задача за домашно ;)   -  person mah    schedule 10.08.2012


Отговори (2)


Не искате value да бъде typedef.

template<bool head, bool... tail>
struct var_and {
    static constexpr bool value = head && var_and<tail...>::value;
};

template<bool b> struct var_and<b> {
    static constexpr bool value = b;
};

Очевидно същото може да се направи за ||.

Оценката на късо съединение няма значение, защото това се занимава само с постоянни изрази, които няма да имат странични ефекти.

Ето още един метод, който спира рекурсивно генериране на типове веднага щом намери невярна стойност, емулиращ вид късо съединение:

template<bool head, bool... tail>
struct var_and { static constexpr bool value = false; };

template<bool... tail> struct var_and<true,tail...> {
    static constexpr bool value = var_and<tail...>::value;
};

template<> struct var_and<true> {
    static constexpr bool value = true;
};

Актуализация за C++17: Използването на израз на сгъване прави това много по-лесно.

template<bool...v> struct var_and {
    static constexpr bool value = (v && ...);
};

Или също така да използвате променлива на шаблон, както предлага enobayram:

template<bool... b> constexpr bool var_and = (b && ...);
person bames53    schedule 09.08.2012
comment
Благодаря за отговора и коригирането на грешката ми с typedef... коригирана във въпроса по-горе. И аз съм съгласен, че оценката на късо съединение няма значение; все пак се интересувам от чисто академични причини. Можете ли да получите това поведение чрез нещо като: <bool h1, bool h2, bool...tail> и след това value = var_and<h1 && h2, tail...>::value;, плюс предполагам друг основен случай? Мислите ли, че компилаторът ще използва това? - person nknight; 10.08.2012
comment
Късо съединение все още ще бъде направено, доколкото оценителят по време на компилация никога не трябва да оценява дясната страна на head && var_and<tail...>::value, ако оценката на лявата страна води до true. Въпреки това компилаторът все още трябва да конструира израза, което означава генериране на типа var_and<tail...>. Тъй като изчислението се извършва като част от конструирането на типа, а не като част от оценяването на ...::value израза, ефективно няма късо съединение с този метод. Въпреки това мисля, че знам начин да го получа. Ще редактирам отговора си. - person bames53; 10.08.2012
comment
Мисля, че можете да подправите C++17 версията още повече, като използвате вградена променлива :) По-малко работа за компилатора и тя става едноредова. - person enobayram; 19.08.2016

Просто имах нужда от нещо подобно, но имам лукса да използвам C++14, така че накрая избрах следното, което вероятно е по-бързо (за компилиране) от приетия отговор:

template <size_t N>
constexpr bool and_all(const bool (&bs) [N]) {
  for(bool b: bs) if(!b) return false;
  return true;
}

Сега, това е constexpr, така че може да се използва в контексти на времето за компилиране, както и по време на изпълнение. Така че можем, например, да го използваме в контекст като some_struct<and_all({true, false, arg_pack...})>

person enobayram    schedule 18.08.2016
comment
Този отговор подобрява (оригиналния) приет отговор чрез внедряване на късо съединение и надхвърля зададения въпрос с разглеждане по време на изпълнение. Въпреки тези подобрения, не промених приетия отговор, защото (1) въпросът ми изрично се отнасяше до C++11 и (2) приетият отговор впоследствие беше актуализиран с помощта на ( C++17) сгъваеми изрази, като template<bool... b> constexpr bool var_and = (... && b);, точно конструкцията, която първоначално търсен. - person nknight; 20.09.2016
comment
Разбира се, приетият отговор е по-изчерпателен, просто исках да дам моите 2 цента. Между другото, за да бъде ясно, този отговор не прилага късо съединение в сайта за повикване, това е функция на ниво език, която е достъпна само при много специфични обстоятелства. - person enobayram; 20.09.2016
comment
Мисля, че все още не съм ясен -- бихте ли уточнили по-подробно? Семантиката на and_all(bs) изглежда идентична с тази на bs[0] && /* ... */ && bs[N-1], така че бих казал, че първото прилага второто. - person nknight; 20.09.2016
comment
@nknight Може би разбираме различни неща от късо съединение. Използвам го, за да намекна за поведението, при което foo() && bar() никога няма да оцени bar(), ако foo() върне false. Но ако напишете and_all(foo(), bar()), и двете ще бъдат оценени, преди да бъдат предадени на and_all. - person enobayram; 21.09.2016
comment
Вашето разбиране е правилното; Сега вярвам, че никое от предложените решения не прилага наистина късо съединение. Измених първоначалния си въпрос с коментар, целящ да изясни тази тънкост. - person nknight; 27.09.2016