Решить проблему восьми королев во время компиляции [закрыто]

39

Можете ли вы решить загадку восьми королев во время компиляции?

Выберите любой подходящий формат вывода.

Меня особенно интересует решение для метапрограммирования шаблонов на C ++, но вы можете использовать языки с похожими конструкциями, как, например, система типов Haskell.

В идеале ваша метапрограмма будет выводить все решения. Нет жесткого кодирования.

Р. Мартиньо Фернандес
источник
Почему вы не допускаете разные языки?
пользователь неизвестен
@user: Потому что меня интересует решение C ++ TMP. Если вы знаете язык, который имеет очень похожие конструкции, вы можете опубликовать ответ.
Р. Мартиньо Фернандес
Могу ли я также использовать систему типов Haskell? AFAIK это должно быть завершено.
FUZxxl
@FUZxxl: Да. Я отредактирую вопрос.
Р. Мартиньо Фернандес
Достаточно ли сделать грубое силовое решение?
перестал поворачиваться против часовой стрелки

Ответы:

50

Моя метапрограмма находит все 92 решения. Они печатаются как сообщения об ошибках:

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

Это означает, что первая королева должна быть помещена в y = 1, вторая в y = 5, третья в y = 8 и так далее.

Во-первых, некоторые полезные мета-функции:

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
};

Затем две интересные мета-функции (обратите внимание на единственное и множественное число):

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 {};

Переменная queensхранит координаты y королев, размещенных на доске до сих пор. В следующих трех переменных хранятся строки и диагонали, которые уже заняты ферзями. xи yдолжно быть само за себя.

Первый аргумент для if_then_elseпроверки, заблокирована ли текущая позиция. Если это так, рекурсия останавливается, возвращая (бессмысленный) результат 0. В противном случае ферзь помещается на доску, и процесс продолжается со следующего столбца.

Когда х достигает 8, мы нашли решение:

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

Поскольку printшаблон не имеет члена solution, компилятор генерирует ошибку.

И наконец, чтобы начать процесс, мы проверяем valueчлена пустой доски:

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

Полная программа может быть найдена в Ideone .

fredoverflow
источник
2
Мне нравится: 1) использование битовых полей для хранения данных, 2) выбор метода вывода.
Р. Мартиньо Фернандес
7
Слишком много удивления для одного ответа.
st0le
Разве он не должен также выводить значения x?
DeadMG
2
@DeadMG Значение x каждого местоположения ферзя - это его позиция в строке (1-8).
Briguy37
22

Я придумал решение, которое использует систему типов Haskell. Я немного погуглил существующее решение проблемы на уровне значений , немного изменил его, а затем поднял до уровня типа. Потребовалось много изобретений. Мне также пришлось включить несколько расширений GHC.

Во-первых, поскольку на уровне типов недопустимы целые числа, мне нужно было заново изобретать натуральные числа, на этот раз в виде типов:

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

Алгоритм, который я адаптировал, делает сложения и вычитания на натуральных объектах, поэтому мне пришлось их заново изобретать. Функции на уровне типов определяются с использованием классов типов. Это требует расширений для нескольких классов типов параметров и функциональных зависимостей. Классы типов не могут «возвращать значения», поэтому мы используем для этого дополнительный параметр, аналогично 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

Рекурсия реализована с помощью утверждений классов, поэтому синтаксис выглядит немного назад.

Далее были логические:

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

И функция для сравнения неравенства:

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

И списки ...

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

ifs также отсутствуют на уровне типа ...

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

И с этим все вспомогательные механизмы, которые я использовал, были на месте. Время заняться самой проблемой!

Начиная с функции для проверки правильности добавления ферзя на существующую доску:

-- 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)]

Обратите внимание на использование утверждений класса для получения промежуточных результатов. Поскольку возвращаемые значения на самом деле являются дополнительным параметром, мы не можем просто вызывать утверждения напрямую друг от друга. Опять же, если вы использовали PROLOG раньше, вам может показаться, что этот стиль немного знаком.

После того, как я сделал несколько изменений, чтобы убрать необходимость в лямбдах (которые я мог бы реализовать, но я решил уйти на другой день), вот как выглядело оригинальное решение:

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]

mapявляется функцией более высокого порядка. Я думал, что реализация мета-функций более высокого порядка будет слишком хлопотной (опять же лямбда-выражения), поэтому я просто выбрал более простое решение: так как я знаю, какие функции будут отображаться, я могу реализовать специализированные версии mapдля каждой, чтобы они не были функции высшего порядка.

-- 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)

И последняя мета-функция может быть написана сейчас:

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

Все, что осталось, это какой-то драйвер, чтобы уговорить механизм проверки типов для выработки решений.

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

Предполагается, что эта метапрограмма работает на контроллере типов, поэтому можно запустить ghciи запросить тип queens eight:

> :t queens eight

Это довольно быстро превысит установленный по умолчанию предел рекурсии (это жалкие 20). Чтобы увеличить этот предел, нам нужно вызвать ghciс -fcontext-stack=Nопцией, где Nжелаемая глубина стека (N = 1000 и пятнадцать минут недостаточно). Я еще не видел этот прогон до завершения, так как он занимает очень много времени, но мне удалось подойти к нему queens four.

На Ideone есть полная программа с некоторыми механизмами для красивой печати типов результатов, но они queens twoмогут работать только без превышения ограничений :(

Р. Мартиньо Фернандес
источник
Помимо интересного решения, это забавная ссылка на то, что можно сделать с помощью логики класса / экземпляра
Майкл Кляйн
11

С, через препроцессор

Я думаю, что комитет ANSI сделал осознанный выбор, чтобы не расширять препроцессор C до точки завершения Тьюринга. В любом случае, он не достаточно мощный, чтобы решить проблему восьми королев. Не в общем смысле.

Но это можно сделать, если вы хотите жестко закодировать счетчики циклов. Конечно, нет реального способа зацикливания, но вы можете использовать самостоятельное включение (через #include __FILE__), чтобы получить ограниченную рекурсию.

#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

Несмотря на ужасающее количество повторяющегося контента, позвольте мне заверить вас, что он действительно решает проблему восьми королев с помощью алгоритма. К сожалению, единственное, что я не смог сделать с препроцессором, - это реализовать общую структуру данных стека. В результате мне пришлось жестко кодировать значение, iгде бы оно ни использовалось, чтобы выбрать другое значение для установки. (В отличие от извлечения значений, что может быть сделано полностью в целом. Вот почему #ifв верхней части файла, который решает, можно ли добавить королеву в текущей позиции, не нужно повторять восемь раз.)

Внутри кода препроцессора, iи jпоказывают текущее положение рассматривается, в то время r, pи nотслеживать, какие ранги и диагонали в настоящее время недоступны для размещения. Тем не менее, он iтакже удваивается как счетчик, отмечающий текущую глубину рекурсии, так что на самом деле все другие значения фактически используют i как своего рода индекс, так что их значения сохраняются при возобновлении из рекурсии. (А также из-за серьезной трудности изменения значения символа препроцессора без его полной замены.)

Скомпилированная программа печатает все 92 решения. Решения встроены непосредственно в исполняемый файл; вывод препроцессора выглядит так:

/* ... #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;
}

Это может быть сделано, хотя это явно не должно быть.

Хлебница
источник
7

Вот решение C ++ 11 без каких-либо шаблонов:

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);

Решение закодировано в виде десятичных цифр, как в ответах FredOverflow. GCC 4.7.1 компилирует вышеуказанный файл в следующий источник сборки с 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

Значение символа places- 84136275, т. Е. Первая ферзь находится на а8, вторая на б4 и т. Д.

хань
источник
0

Шаблон C ++, только с одним шаблоном класса определяется:

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;
}

поэтому сообщение об ошибке будет выглядеть так:

ошибка C2440: «приведение типа»: невозможно преобразовать из «int» в «char [15863724]»

DU Jiaen
источник