Rozwiąż problem ośmiu królowych w czasie kompilacji [zamknięte]

39

Czy potrafisz rozwiązać zagadkę z ośmioma królowymi w czasie kompilacji?

Wybierz dowolny odpowiedni format wyjściowy.

Szczególnie interesuje mnie rozwiązanie do metaprogramowania szablonów C ++, ale możesz używać języków o podobnych konstrukcjach, takich jak na przykład system typów Haskell.

Idealnie twój metaprogram wyprowadziłby wszystkie rozwiązania. Bez kodowania.

R. Martinho Fernandes
źródło
Dlaczego nie pozwalasz na różne języki?
użytkownik nieznany
@ użytkownik: Ponieważ interesuje mnie rozwiązanie C ++ TMP. Jeśli znasz język o bardzo podobnych konstrukcjach, możesz jednak opublikować odpowiedź.
R. Martinho Fernandes,
Czy mogę również użyć systemu typów Haskell? AFAIK powinien być kompletny.
FUZxxl,
@FUZxxl: Tak. Zmienię pytanie.
R. Martinho Fernandes
Czy wystarczy rozwiązanie brutalnej siły?
przestał się obracać w lewo o

Odpowiedzi:

50

Mój metaprogram zawiera wszystkie 92 rozwiązania. Są drukowane jako komunikaty o błędach:

error: 'solution' is not a member of 'print<15863724>'

Oznacza to, że pierwszą królową należy umieścić na y = 1, drugą na y = 5, trzecią na y = 8 i tak dalej.

Po pierwsze, kilka przydatnych meta-funkcji:

template <typename T>
struct return_
{
    typedef T type;
};

template <bool Condition, typename Then, typename Else>
struct if_then_else;

template <typename Then, typename Else>
struct if_then_else<true, Then, Else> : return_<Then> {};

template <typename Then, typename Else>
struct if_then_else<false, Then, Else> : return_<Else> {};

template <int N>
struct constant
{
    enum { value = N };
};

template <int N>
struct print
{
    // empty body -> member access yields a compiler error involving N
};

Następnie dwie interesujące meta-funkcje (zwróć uwagę na liczbę pojedynczą i mnogą):

template <int queens, int rows, int sums, int difs, int x, int y>
struct put_queen;

template <int queens, int rows, int sums, int difs, int x>
struct put_queens : constant
     < put_queen<queens, rows, sums, difs, x, 1>::value
     + put_queen<queens, rows, sums, difs, x, 2>::value
     + put_queen<queens, rows, sums, difs, x, 3>::value
     + put_queen<queens, rows, sums, difs, x, 4>::value
     + put_queen<queens, rows, sums, difs, x, 5>::value
     + put_queen<queens, rows, sums, difs, x, 6>::value
     + put_queen<queens, rows, sums, difs, x, 7>::value
     + put_queen<queens, rows, sums, difs, x, 8>::value > {};

template <int queens, int rows, int sums, int difs, int x, int y>
struct put_queen : if_then_else<
    rows & (1 << y) || sums & (1 << (x + y)) || difs & (1 << (8 + x - y)),
    constant<0>,
    put_queens<queens * 10 + y, rows | (1 << y), sums | (1 << (x + y)),
               difs | (1 << (8 + x - y)), x + 1>
>::type {};

Zmienna queensprzechowuje współrzędne y królowych umieszczonych do tej pory na planszy. Poniższe trzy zmienne przechowują wiersze i przekątne, które są już zajęte przez królowe. xi ypowinny być zrozumiałe.

Pierwszy argument if_then_elsesprawdzający, czy bieżąca pozycja jest zablokowana. Jeśli tak, rekursja kończy się, zwracając (bez znaczenia) wynik 0. W przeciwnym razie królowa jest umieszczana na planszy, a proces jest kontynuowany z następną kolumną.

Gdy x osiągnie 8, znaleźliśmy rozwiązanie:

template <int queens, int rows, int sums, int difs>
struct put_queens<queens, rows, sums, difs, 8>
{
    enum { value = print<queens>::solution };
};

Ponieważ printszablon nie ma elementu solution, kompilator generuje błąd.

I wreszcie, aby rozpocząć proces, sprawdzamy valueczłonka pustej planszy:

int go = put_queens<0, 0, 0, 0, 0>::value;

Kompletny program można znaleźć na stronie ideone .

fredoverflow
źródło
2
Lubię: 1) używanie pól bitowych do przechowywania danych, 2) wybór metody wyjściowej.
R. Martinho Fernandes,
7
Zbyt wiele niesamowitości na jedną odpowiedź.
st0le
Czy nie powinno to również generować wartości x?
DeadMG
2
@DeadMG Wartość x każdej lokalizacji królowej to jej pozycja w ciągu (1-8).
Briguy37
22

Wymyśliłem rozwiązanie, które wykorzystuje system typu Haskell. Poszukałem trochę istniejącego rozwiązania problemu na poziomie wartości , nieco go zmieniłem, a następnie podniosłem do poziomu typu. Wymyśliłem wiele na nowo. Musiałem także włączyć kilka rozszerzeń GHC.

Po pierwsze, ponieważ liczby całkowite nie są dozwolone na poziomie typu, musiałem ponownie odkryć liczby naturalne, tym razem jako typy:

data Zero -- type that represents zero
data S n  -- type constructor that constructs the successor of another natural number
-- Some numbers shortcuts
type One = S Zero
type Two = S One
type Three = S Two
type Four = S Three
type Five = S Four
type Six = S Five
type Seven = S Six
type Eight = S Seven

Algorytm, który zaadaptowałem, dodaje i odejmuje wartości naturalne, więc musiałem je też wymyślić na nowo. Funkcje na poziomie typu są definiowane za pomocą klas typów. Wymaga to rozszerzenia wielu klas typów parametrów i zależności funkcjonalnych. Klasy typów nie mogą „zwracać wartości”, dlatego używamy do tego dodatkowego parametru, w sposób podobny do PROLOG.

class Add a b r | a b -> r -- last param is the result
instance Add Zero b b                     -- 0 + b = b
instance (Add a b r) => Add (S a) b (S r) -- S(a) + b = S(a + b)

class Sub a b r | a b -> r
instance Sub a Zero a                     -- a - 0 = a
instance (Sub a b r) => Sub (S a) (S b) r -- S(a) - S(b) = a - b

Rekurencja jest implementowana z asercjami klas, więc składnia wygląda nieco wstecz.

Następne były booleany:

data True  -- type that represents truth
data False -- type that represents falsehood

I funkcja do porównywania nierówności:

class NotEq a b r | a b -> r
instance NotEq Zero Zero False                -- 0 /= 0 = False
instance NotEq (S a) Zero True                -- S(a) /= 0 = True
instance NotEq Zero (S a) True                -- 0 /= S(a) = True
instance (NotEq a b r) => NotEq (S a) (S b) r -- S(a) /= S(b) = a /= b

I listy ...

data Nil
data h ::: t
infixr 0 :::

class Append xs ys r | xs ys -> r
instance Append Nil ys ys                                       -- [] ++ _ = []
instance (Append xs ys rec) => Append (x ::: xs) ys (x ::: rec) -- (x:xs) ++ ys = x:(xs ++ ys)

class Concat xs r | xs -> r
instance Concat Nil Nil                                         -- concat [] = []
instance (Concat xs rec, Append x rec r) => Concat (x ::: xs) r -- concat (x:xs) = x ++ concat xs

class And l r | l -> r
instance And Nil True                    -- and [] = True
instance And (False ::: t) False         -- and (False:_) = False
instance (And t r) => And (True ::: t) r -- and (True:t) = and t

ifbrakuje również na poziomie typu ...

class Cond c t e r | c t e -> r
instance Cond True t e t  -- cond True t _ = t
instance Cond False t e e -- cond False _ e = e

Dzięki temu wszystkie maszyny pomocnicze, których użyłem, były na miejscu. Czas rozwiązać sam problem!

Rozpoczęcie od funkcji sprawdzania, czy dodanie królowej do istniejącej planszy jest w porządku:

-- Testing if it's safe to add a queen
class Safe x b n r | x b n -> r
instance Safe x Nil n True    -- safe x [] n = True
instance (Safe x y (S n) rec,
          Add c n cpn, Sub c n cmn,
          NotEq x c c1, NotEq x cpn c2, NotEq x cmn c3,
          And (c1 ::: c2 ::: c3 ::: rec ::: Nil) r) => Safe x (c ::: y) n r
    -- safe x (c:y) n = and [ x /= c , x /= c + n , x /= c - n , safe x y (n+1)]

Zwróć uwagę na użycie asercji klasowych w celu uzyskania wyników pośrednich. Ponieważ zwracane wartości są w rzeczywistości dodatkowym parametrem, nie możemy po prostu wywoływać asercji bezpośrednio od siebie. Ponownie, jeśli używałeś PROLOGA, możesz uznać ten styl za nieco znajomy.

Po wprowadzeniu kilku zmian w celu wyeliminowania potrzeby używania lambda (które mogłem wdrożyć, ale postanowiłem wyjechać na kolejny dzień), tak wyglądało oryginalne rozwiązanie:

queens 0 = [[]]
-- The original used the list monad. I "unrolled" bind into concat & map.
queens n = concat $ map f $ queens (n-1)
g y x = if safe x y 1 then [x:y] else []
f y = concat $ map (g y) [1..8]

mapjest funkcją wyższego rzędu. Myślałem, że zaimplementowanie meta-funkcji wyższego rzędu byłoby zbyt dużym problemem (znowu lambdas), więc po prostu wybrałem prostsze rozwiązanie: ponieważ wiem, które funkcje będą mapowane, mogę zaimplementować mapdla nich specjalizowane wersje , aby nie były funkcje wyższego rzędu.

-- Auxiliary meta-functions
class G y x r | y x -> r
instance (Safe x y One s, Cond s ((x ::: y) ::: Nil) Nil r) => G y x r

class MapG y l r | y l -> r
instance MapG y Nil Nil
instance (MapG y xs rec, G y x g) => MapG y (x ::: xs) (g ::: rec)

-- Shortcut for [1..8]
type OneToEight = One ::: Two ::: Three ::: Four ::: Five ::: Six ::: Seven ::: Eight ::: Nil

class F y r | y -> r
instance (MapG y OneToEight m, Concat m r) => F y r -- f y = concat $ map (g y) [1..8]

class MapF l r | l -> r
instance MapF Nil Nil
instance (MapF xs rec, F x f) => MapF (x ::: xs) (f ::: rec)

A ostatnią meta-funkcję można teraz napisać:

class Queens n r | n -> r
instance Queens Zero (Nil ::: Nil)
instance (Queens n rec, MapF rec m, Concat m r) => Queens (S n) r

Pozostało po prostu jakiś sterownik, który nakłonił maszynę do sprawdzania typu do wypracowania rozwiązań.

-- dummy value of type Eight
eight = undefined :: Eight
-- dummy function that asserts the Queens class
queens :: Queens n r => n -> r
queens = const undefined

Ten metaprogram ma działać na narzędziu sprawdzania typu, więc można odpalić ghcii zapytać o typ queens eight:

> :t queens eight

To dość szybko przekroczy domyślny limit rekurencji (to marne 20). Aby zwiększyć ten limit, musimy wywołać ghciz -fcontext-stack=Nopcją, gdzie Njest pożądana głębokość stosu (N = 1000 i piętnaście minut to za mało). Nie widziałem jeszcze tego ukończenia, ponieważ zajmuje to bardzo dużo czasu, ale udało mi się do niego dotrzeć queens four.

Jest idealny program na ideone z niektórymi maszynami do ładnego drukowania typów wyników, ale queens twomoże działać tylko bez przekraczania limitów :(

R. Martinho Fernandes
źródło
Oprócz ciekawego rozwiązania, jest to zabawne odniesienie do tego, co można zrobić za pomocą logiki klasy / instancji
Michael Klein
11

C za pośrednictwem preprocesora

Myślę, że komitet ANSI dokonał świadomego wyboru, aby nie rozszerzać preprocesora C do tego stopnia, że ​​jest całkowicie ukończony przez Turinga. W każdym razie nie jest wystarczająco silny, aby rozwiązać problem ośmiu królowych. Nie w jakikolwiek ogólny sposób.

Ale można to zrobić, jeśli chcesz na stałe zakodować liczniki pętli. Oczywiście nie ma prawdziwego sposobu zapętlenia, ale możesz użyć samowyłączenia (via #include __FILE__), aby uzyskać ograniczony rodzaj rekurencji.

#ifdef i
# if (r_(i) & 1 << j_(i)) == 0 && (p_(i) & 1 << i + j_(i)) == 0 \
                               && (n_(i) & 1 << 7 + i - j_(i)) == 0
#  if i == 0
#   undef i
#   define i 1
#   undef r1
#   undef p1
#   undef n1
#   define r1 (r0 | (1 << j0))
#   define p1 (p0 | (1 << j0))
#   define n1 (n0 | (1 << 7 - j0))
#   undef j1
#   define j1 0
#   include __FILE__
#   undef j1
#   define j1 1
#   include __FILE__
#   undef j1
#   define j1 2
#   include __FILE__
#   undef j1
#   define j1 3
#   include __FILE__
#   undef j1
#   define j1 4
#   include __FILE__
#   undef j1
#   define j1 5
#   include __FILE__
#   undef j1
#   define j1 6
#   include __FILE__
#   undef j1
#   define j1 7
#   include __FILE__
#   undef i
#   define i 0
#  elif i == 1
#   undef i
#   define i 2
#   undef r2
#   undef p2
#   undef n2
#   define r2 (r1 | (1 << j1))
#   define p2 (p1 | (1 << 1 + j1))
#   define n2 (n1 | (1 << 8 - j1))
#   undef j2
#   define j2 0
#   include __FILE__
#   undef j2
#   define j2 1
#   include __FILE__
#   undef j2
#   define j2 2
#   include __FILE__
#   undef j2
#   define j2 3
#   include __FILE__
#   undef j2
#   define j2 4
#   include __FILE__
#   undef j2
#   define j2 5
#   include __FILE__
#   undef j2
#   define j2 6
#   include __FILE__
#   undef j2
#   define j2 7
#   include __FILE__
#   undef i
#   define i 1
#  elif i == 2
#   undef i
#   define i 3
#   undef r3
#   undef p3
#   undef n3
#   define r3 (r2 | (1 << j2))
#   define p3 (p2 | (1 << 2 + j2))
#   define n3 (n2 | (1 << 9 - j2))
#   undef j3
#   define j3 0
#   include __FILE__
#   undef j3
#   define j3 1
#   include __FILE__
#   undef j3
#   define j3 2
#   include __FILE__
#   undef j3
#   define j3 3
#   include __FILE__
#   undef j3
#   define j3 4
#   include __FILE__
#   undef j3
#   define j3 5
#   include __FILE__
#   undef j3
#   define j3 6
#   include __FILE__
#   undef j3
#   define j3 7
#   include __FILE__
#   undef i
#   define i 2
#  elif i == 3
#   undef i
#   define i 4
#   undef r4
#   undef p4
#   undef n4
#   define r4 (r3 | (1 << j3))
#   define p4 (p3 | (1 << 3 + j3))
#   define n4 (n3 | (1 << 10 - j3))
#   undef j4
#   define j4 0
#   include __FILE__
#   undef j4
#   define j4 1
#   include __FILE__
#   undef j4
#   define j4 2
#   include __FILE__
#   undef j4
#   define j4 3
#   include __FILE__
#   undef j4
#   define j4 4
#   include __FILE__
#   undef j4
#   define j4 5
#   include __FILE__
#   undef j4
#   define j4 6
#   include __FILE__
#   undef j4
#   define j4 7
#   include __FILE__
#   undef i
#   define i 3
#  elif i == 4
#   undef i
#   define i 5
#   undef r5
#   undef p5
#   undef n5
#   define r5 (r4 | (1 << j4))
#   define p5 (p4 | (1 << 4 + j4))
#   define n5 (n4 | (1 << 11 - j4))
#   undef j5
#   define j5 0
#   include __FILE__
#   undef j5
#   define j5 1
#   include __FILE__
#   undef j5
#   define j5 2
#   include __FILE__
#   undef j5
#   define j5 3
#   include __FILE__
#   undef j5
#   define j5 4
#   include __FILE__
#   undef j5
#   define j5 5
#   include __FILE__
#   undef j5
#   define j5 6
#   include __FILE__
#   undef j5
#   define j5 7
#   include __FILE__
#   undef i
#   define i 4
#  elif i == 5
#   undef i
#   define i 6
#   undef r6
#   undef p6
#   undef n6
#   define r6 (r5 | (1 << j5))
#   define p6 (p5 | (1 << 5 + j5))
#   define n6 (n5 | (1 << 12 - j5))
#   undef j6
#   define j6 0
#   include __FILE__
#   undef j6
#   define j6 1
#   include __FILE__
#   undef j6
#   define j6 2
#   include __FILE__
#   undef j6
#   define j6 3
#   include __FILE__
#   undef j6
#   define j6 4
#   include __FILE__
#   undef j6
#   define j6 5
#   include __FILE__
#   undef j6
#   define j6 6
#   include __FILE__
#   undef j6
#   define j6 7
#   include __FILE__
#   undef i
#   define i 5
#  elif i == 6
#   undef i
#   define i 7
#   undef r7
#   undef p7
#   undef n7
#   define r7 (r6 | (1 << j6))
#   define p7 (p6 | (1 << 6 + j6))
#   define n7 (n6 | (1 << 13 - j6))
#   undef j7
#   define j7 0
#   include __FILE__
#   undef j7
#   define j7 1
#   include __FILE__
#   undef j7
#   define j7 2
#   include __FILE__
#   undef j7
#   define j7 3
#   include __FILE__
#   undef j7
#   define j7 4
#   include __FILE__
#   undef j7
#   define j7 5
#   include __FILE__
#   undef j7
#   define j7 6
#   include __FILE__
#   undef j7
#   define j7 7
#   include __FILE__
#   undef i
#   define i 6
#  elif i == 7
    printf("(1 %d) (2 %d) (3 %d) (4 %d) (5 %d) (6 %d) (7 %d) (8 %d)\n",
           j0 + 1, j1 + 1, j2 + 1, j3 + 1, j4 + 1, j5 + 1, j6 + 1, j7 + 1);
#  endif
# endif
#else
#include <stdio.h>
#define _cat(a, b) a ## b
#define j_(i) _cat(j, i)
#define n_(i) _cat(n, i)
#define p_(i) _cat(p, i)
#define r_(i) _cat(r, i)
int main(void)
{
# define i 0
# define j0 0
# include __FILE__
# undef j0
# define j0 1
# include __FILE__
# undef j0
# define j0 2
# include __FILE__
# undef j0
# define j0 3
# include __FILE__
# undef j0
# define j0 4
# include __FILE__
# undef j0
# define j0 5
# include __FILE__
# undef j0
# define j0 6
# include __FILE__
# undef j0
# define j0 7
# include __FILE__
# undef j0
    return 0;
}
#endif

Pomimo przerażającej ilości powtarzających się treści, zapewniam cię, że naprawdę rozwiązuje ona problem ośmiu królowych algorytmicznie. Niestety jedyną rzeczą, której nie mogłem zrobić z preprocesorem, jest implementacja ogólnej struktury danych stosu push-down. Rezultat jest taki, że musiałem zakodować wartość, igdziekolwiek była używana, aby wybrać inną wartość do ustawienia. (W przeciwieństwie do pobierania wartości, które można wykonać całkowicie ogólnie. Dlatego #ifu góry pliku, który decyduje o tym, czy można dodać królową w bieżącej pozycji, nie trzeba powtarzać osiem razy).

W kodzie preprocesora, ii jwskazują aktualną pozycję rozważane, podczas r, pi nktórego śledzić szeregi i przekątne są aktualnie niedostępne miejsca. Jednak irównież pełni funkcję licznika zaznaczającego bieżącą głębokość rekurencji, więc tak naprawdę wszystkie inne wartości faktycznie używają i jako rodzaju indeksu dolnego, dzięki czemu ich wartości są zachowywane po wznowieniu z rekurencji. (A także z powodu poważnej trudności w modyfikacji wartości symbolu preprocesora bez jego całkowitego zastąpienia.)

Skompilowany program drukuje wszystkie 92 rozwiązania. Rozwiązania są osadzone bezpośrednio w pliku wykonywalnym; Wyjście preprocesora wygląda następująco:

/* ... #included content from <stdio.h> ... */
int main(void)
{
    printf("(1 %d) (2 %d) (3 %d) (4 %d) (5 %d) (6 %d) (7 %d) (8 %d)\n",
           0 + 1, 4 + 1, 7 + 1, 5 + 1, 2 + 1, 6 + 1, 1 + 1, 3 + 1);
    printf("(1 %d) (2 %d) (3 %d) (4 %d) (5 %d) (6 %d) (7 %d) (8 %d)\n",
           0 + 1, 5 + 1, 7 + 1, 2 + 1, 6 + 1, 3 + 1, 1 + 1, 4 + 1);
    printf("(1 %d) (2 %d) (3 %d) (4 %d) (5 %d) (6 %d) (7 %d) (8 %d)\n",
           0 + 1, 6 + 1, 3 + 1, 5 + 1, 7 + 1, 1 + 1, 4 + 1, 2 + 1);
    /* ... 88 more solutions ... */
    printf("(1 %d) (2 %d) (3 %d) (4 %d) (5 %d) (6 %d) (7 %d) (8 %d)\n",
           7 + 1, 3 + 1, 0 + 1, 2 + 1, 5 + 1, 1 + 1, 6 + 1, 4 + 1);
    return 0;
}

Można to zrobić, nawet jeśli wyraźnie nie powinno.

chlebak
źródło
7

Oto rozwiązanie C ++ 11 bez żadnych szablonów:

constexpr int trypos(
    int work, int col, int row, int rows, int diags1, int diags2,
    int rowbit, int diag1bit, int diag2bit);

constexpr int place(
    int result, int work, int col, int row, int rows, int diags1, int diags2)
{
    return result != 0 ? result
        : col == 8 ? work
        : row == 8 ? 0
        : trypos(work, col, row, rows, diags1, diags2,
                 1 << row, 1 << (7 + col - row), 1 << (14 - col - row));
}

constexpr int trypos(
    int work, int col, int row, int rows, int diags1, int diags2,
    int rowbit, int diag1bit, int diag2bit)
{
    return !(rows & rowbit) && !(diags1 & diag1bit) && !(diags2 & diag2bit)
        ? place(
            place(0, work*10 + 8-row, col + 1, 0,
                  rows | rowbit, diags1 | diag1bit, diags2 | diag2bit),
            work, col, row + 1, rows, diags1, diags2)
        : place(0, work, col, row + 1, rows, diags1, diags2);
}

int places = place(0, 0, 0, 0, 0, 0, 0);

Rozwiązanie jest zakodowane jako cyfry dziesiętne, jak w odpowiedziach FredOverflow. GCC 4.7.1 kompiluje powyższy plik do następującego źródła zestawu z g++ -S -std=c++11 8q.cpp:

    .file   "8q.cpp"
    .globl  places
    .data
    .align 4
    .type   places, @object
    .size   places, 4
places:
    .long   84136275
    .ident  "GCC: (GNU) 4.7.1"
    .section    .note.GNU-stack,"",@progbits

Wartość tego symbolu placesto 84136275, tj. Pierwsza królowa jest na a8, druga na b4 itd.

Han
źródło
0

Szablon c ++, z zdefiniowaną tylko jedną klasą szablonów:

template <int N, int mask, int mask2, int mask3, int remainDigit, bool fail>
struct EQ;

template <int N, int mask, int mask2, int mask3>
struct EQ<N, mask, mask2, mask3, 0, false> {
    enum _ { Output = (char [N])1 };
};

template <int N, int mask, int mask2, int mask3, int i>
struct EQ<N, mask, mask2, mask3, i, true> { };

template <int N, int mask, int mask2, int mask3, int i>
struct EQ<N, mask, mask2, mask3, i, false> {
    enum _ { _ = 
             sizeof(EQ<N*10+1, mask|(1<<1), mask2|(1<<(1+i)), mask3|(1<<(1+8-i)), i-1, 
               (bool)(mask&(1<<1)) || (bool)(mask2&(1<<(1+i))) || (bool)(mask3&(1<<(1+8-i)))>) +
             sizeof(EQ<N*10+2, mask|(1<<2), mask2|(1<<(2+i)), mask3|(1<<(2+8-i)), i-1, 
               (bool)(mask&(1<<2)) || (bool)(mask2&(1<<(2+i))) || (bool)(mask3&(1<<(2+8-i)))>) +
             sizeof(EQ<N*10+3, mask|(1<<3), mask2|(1<<(3+i)), mask3|(1<<(3+8-i)), i-1, 
               (bool)(mask&(1<<3)) || (bool)(mask2&(1<<(3+i))) || (bool)(mask3&(1<<(3+8-i)))>) +
             sizeof(EQ<N*10+4, mask|(1<<4), mask2|(1<<(4+i)), mask3|(1<<(4+8-i)), i-1, 
               (bool)(mask&(1<<4)) || (bool)(mask2&(1<<(4+i))) || (bool)(mask3&(1<<(4+8-i)))>) +
             sizeof(EQ<N*10+5, mask|(1<<5), mask2|(1<<(5+i)), mask3|(1<<(5+8-i)), i-1, 
               (bool)(mask&(1<<5)) || (bool)(mask2&(1<<(5+i))) || (bool)(mask3&(1<<(5+8-i)))>) +
             sizeof(EQ<N*10+6, mask|(1<<6), mask2|(1<<(6+i)), mask3|(1<<(6+8-i)), i-1, 
               (bool)(mask&(1<<6)) || (bool)(mask2&(1<<(6+i))) || (bool)(mask3&(1<<(6+8-i)))>) +
             sizeof(EQ<N*10+7, mask|(1<<7), mask2|(1<<(7+i)), mask3|(1<<(7+8-i)), i-1, 
               (bool)(mask&(1<<7)) || (bool)(mask2&(1<<(7+i))) || (bool)(mask3&(1<<(7+8-i)))>) +
             sizeof(EQ<N*10+8, mask|(1<<8), mask2|(1<<(8+i)), mask3|(1<<(8+8-i)), i-1, 
               (bool)(mask&(1<<8)) || (bool)(mask2&(1<<(8+i))) || (bool)(mask3&(1<<(8+8-i)))>)};
};
int main(int argc, _TCHAR* argv[])
{
    // output all solutions to eight queens problems as error messages
    sizeof(EQ<0, 0, 0, 0, 8, false>);
    return 0;
}

więc komunikat o błędzie będzie wyglądał następująco:

błąd C2440: „rzutowanie typu”: nie można przekonwertować z „int” na „char [15863724]”

DU Jiaen
źródło