C ++ şablonları Turing tamamlandı mı?


Yanıtlar:


110

Misal

#include <iostream>

template <int N> struct Factorial
{
    enum { val = Factorial<N-1>::val * N };
};

template<>
struct Factorial<0>
{
    enum { val = 1 };
};

int main()
{
    // Note this value is generated at compile time.
    // Also note that most compilers have a limit on the depth of the recursion available.
    std::cout << Factorial<4>::val << "\n";
}

Bu biraz eğlenceliydi ama pek pratik değildi.

Sorunun ikinci bölümünü yanıtlamak için:
Bu gerçek pratikte yararlı mı?

Kısa Cevap: Sırala.

Uzun Cevap: Evet, ancak yalnızca bir şablon arka plan programı iseniz.

Başkalarının kullanması (yani bir kitaplık) için gerçekten yararlı olan şablon meta programlamayı kullanarak iyi bir programlamayı ortaya çıkarmak gerçekten çok zordur (ancak yapılabilir). Arttırmaya yardımcı olmak için MPL aka (Meta Programlama Kitaplığı) bile vardır . Ancak şablon kodunuzdaki bir derleyici hatasını ayıklamayı deneyin ve uzun ve zorlu bir yolculuğa çıkacaksınız.

Ancak yararlı bir şey için kullanılmasının iyi bir pratik örneği:

Scott Meyers şablon oluşturma olanaklarını kullanarak C ++ dilinin uzantıları üzerinde çalışıyor (bu terimi gevşek bir şekilde kullanıyorum). Sen burada çalışmaları hakkında okuyabilir Zorlamayı Kod Özellikleri '


36
Dang oraya gitti kavramlar (poof)
Martin York

5
Sağlanan örnekle ilgili sadece küçük bir sorunum var - C ++ 'ın şablon sisteminin (tam) Turing tamlığından yararlanmıyor. Faktöriyel, turing-tamamlanmamış İlkel özyinelemeli işlevler kullanılarak da bulunabilir
Dalibor Frivaldsky

4
a ve şimdi kavramlarımız var lite
nurettin

1
2017'de konseptleri daha da geriye götürüyoruz. İşte 2020 için umut.
DeiDei

2
@MarkKegel 12 yıl sonra: D
Victor

181

C ++ 11'de bir turing makinesi yaptım. C ++ 11'in eklediği özellikler turing makinesi için gerçekten önemli değildir. Sadece, sapkın makro metaprogramlama kullanmak yerine, değişken şablonlar kullanarak keyfi uzunluk kural listeleri sağlar :). Koşulların adları, standart çıktıda bir diyagram oluşturmak için kullanılır. örneği kısa tutmak için bu kodu kaldırdım.

#include <iostream>

template<bool C, typename A, typename B>
struct Conditional {
    typedef A type;
};

template<typename A, typename B>
struct Conditional<false, A, B> {
    typedef B type;
};

template<typename...>
struct ParameterPack;

template<bool C, typename = void>
struct EnableIf { };

template<typename Type>
struct EnableIf<true, Type> {
    typedef Type type;
};

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

// define a type list 
template<typename...>
struct TypeList;

template<typename T, typename... TT>
struct TypeList<T, TT...>  {
    typedef T type;
    typedef TypeList<TT...> tail;
};

template<>
struct TypeList<> {

};

template<typename List>
struct GetSize;

template<typename... Items>
struct GetSize<TypeList<Items...>> {
    enum { value = sizeof...(Items) };
};

template<typename... T>
struct ConcatList;

template<typename... First, typename... Second, typename... Tail>
struct ConcatList<TypeList<First...>, TypeList<Second...>, Tail...> {
    typedef typename ConcatList<TypeList<First..., Second...>, 
                                Tail...>::type type;
};

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

template<typename NewItem, typename List>
struct AppendItem;

template<typename NewItem, typename...Items>
struct AppendItem<NewItem, TypeList<Items...>> {
    typedef TypeList<Items..., NewItem> type;
};

template<typename NewItem, typename List>
struct PrependItem;

template<typename NewItem, typename...Items>
struct PrependItem<NewItem, TypeList<Items...>> {
    typedef TypeList<NewItem, Items...> type;
};

template<typename List, int N, typename = void>
struct GetItem {
    static_assert(N > 0, "index cannot be negative");
    static_assert(GetSize<List>::value > 0, "index too high");
    typedef typename GetItem<typename List::tail, N-1>::type type;
};

template<typename List>
struct GetItem<List, 0> {
    static_assert(GetSize<List>::value > 0, "index too high");
    typedef typename List::type type;
};

template<typename List, template<typename, typename...> class Matcher, typename... Keys>
struct FindItem {
    static_assert(GetSize<List>::value > 0, "Could not match any item.");
    typedef typename List::type current_type;
    typedef typename Conditional<Matcher<current_type, Keys...>::value, 
                                 Identity<current_type>, // found!
                                 FindItem<typename List::tail, Matcher, Keys...>>
        ::type::type type;
};

template<typename List, int I, typename NewItem>
struct ReplaceItem {
    static_assert(I > 0, "index cannot be negative");
    static_assert(GetSize<List>::value > 0, "index too high");
    typedef typename PrependItem<typename List::type, 
                             typename ReplaceItem<typename List::tail, I-1,
                                                  NewItem>::type>
        ::type type;
};

template<typename NewItem, typename Type, typename... T>
struct ReplaceItem<TypeList<Type, T...>, 0, NewItem> {
    typedef TypeList<NewItem, T...> type;
};

enum Direction {
    Left = -1,
    Right = 1
};

template<typename OldState, typename Input, typename NewState, 
         typename Output, Direction Move>
struct Rule {
    typedef OldState old_state;
    typedef Input input;
    typedef NewState new_state;
    typedef Output output;
    static Direction const direction = Move;
};

template<typename A, typename B>
struct IsSame {
    enum { value = false }; 
};

template<typename A>
struct IsSame<A, A> {
    enum { value = true };
};

template<typename Input, typename State, int Position>
struct Configuration {
    typedef Input input;
    typedef State state;
    enum { position = Position };
};

template<int A, int B>
struct Max {
    enum { value = A > B ? A : B };
};

template<int n>
struct State {
    enum { value = n };
    static char const * name;
};

template<int n>
char const* State<n>::name = "unnamed";

struct QAccept {
    enum { value = -1 };
    static char const* name;
};

struct QReject {
    enum { value = -2 };
    static char const* name; 
};

#define DEF_STATE(ID, NAME) \
    typedef State<ID> NAME ; \
    NAME :: name = #NAME ;

template<int n>
struct Input {
    enum { value = n };
    static char const * name;

    template<int... I>
    struct Generate {
        typedef TypeList<Input<I>...> type;
    };
};

template<int n>
char const* Input<n>::name = "unnamed";

typedef Input<-1> InputBlank;

#define DEF_INPUT(ID, NAME) \
    typedef Input<ID> NAME ; \
    NAME :: name = #NAME ;

template<typename Config, typename Transitions, typename = void> 
struct Controller {
    typedef Config config;
    enum { position = config::position };

    typedef typename Conditional<
        static_cast<int>(GetSize<typename config::input>::value) 
            <= static_cast<int>(position),
        AppendItem<InputBlank, typename config::input>,
        Identity<typename config::input>>::type::type input;
    typedef typename config::state state;

    typedef typename GetItem<input, position>::type cell;

    template<typename Item, typename State, typename Cell>
    struct Matcher {
        typedef typename Item::old_state checking_state;
        typedef typename Item::input checking_input;
        enum { value = IsSame<State, checking_state>::value && 
                       IsSame<Cell,  checking_input>::value
        };
    };
    typedef typename FindItem<Transitions, Matcher, state, cell>::type rule;

    typedef typename ReplaceItem<input, position, typename rule::output>::type new_input;
    typedef typename rule::new_state new_state;
    typedef Configuration<new_input, 
                          new_state, 
                          Max<position + rule::direction, 0>::value> new_config;

    typedef Controller<new_config, Transitions> next_step;
    typedef typename next_step::end_config end_config;
    typedef typename next_step::end_input end_input;
    typedef typename next_step::end_state end_state;
    enum { end_position = next_step::position };
};

template<typename Input, typename State, int Position, typename Transitions>
struct Controller<Configuration<Input, State, Position>, Transitions, 
                  typename EnableIf<IsSame<State, QAccept>::value || 
                                    IsSame<State, QReject>::value>::type> {
    typedef Configuration<Input, State, Position> config;
    enum { position = config::position };
    typedef typename Conditional<
        static_cast<int>(GetSize<typename config::input>::value) 
            <= static_cast<int>(position),
        AppendItem<InputBlank, typename config::input>,
        Identity<typename config::input>>::type::type input;
    typedef typename config::state state;

    typedef config end_config;
    typedef input end_input;
    typedef state end_state;
    enum { end_position = position };
};

template<typename Input, typename Transitions, typename StartState>
struct TuringMachine {
    typedef Input input;
    typedef Transitions transitions;
    typedef StartState start_state;

    typedef Controller<Configuration<Input, StartState, 0>, Transitions> controller;
    typedef typename controller::end_config end_config;
    typedef typename controller::end_input end_input;
    typedef typename controller::end_state end_state;
    enum { end_position = controller::end_position };
};

#include <ostream>

template<>
char const* Input<-1>::name = "_";

char const* QAccept::name = "qaccept";
char const* QReject::name = "qreject";

int main() {
    DEF_INPUT(1, x);
    DEF_INPUT(2, x_mark);
    DEF_INPUT(3, split);

    DEF_STATE(0, start);
    DEF_STATE(1, find_blank);
    DEF_STATE(2, go_back);

    /* syntax:  State, Input, NewState, Output, Move */
    typedef TypeList< 
        Rule<start, x, find_blank, x_mark, Right>,
        Rule<find_blank, x, find_blank, x, Right>,
        Rule<find_blank, split, find_blank, split, Right>,
        Rule<find_blank, InputBlank, go_back, x, Left>,
        Rule<go_back, x, go_back, x, Left>,
        Rule<go_back, split, go_back, split, Left>,
        Rule<go_back, x_mark, start, x, Right>,
        Rule<start, split, QAccept, split, Left>> rules;

    /* syntax: initial input, rules, start state */
    typedef TuringMachine<TypeList<x, x, x, x, split>, rules, start> double_it;
    static_assert(IsSame<double_it::end_input, 
                         TypeList<x, x, x, x, split, x, x, x, x>>::value, 
                "Hmm... This is borky!");
}

131
Çok fazla zamanın var.
Mark Kegel

2
Tüm bu parantezlerin yerini alan belirli bir sözcük haricinde lisp'e benziyor.
Simon Kuang

1
Tam kaynak, meraklı okuyucu için bir yerlerde halka açık mı? :)
OJFord

1
Sadece girişim daha fazla krediyi hak ediyor :-) Bu kod derler (gcc-4.9) ancak çıktı vermez - blog yazısı gibi biraz daha fazla bilgi harika olurdu.
Alfred Bratterud

2
@OllieFord Bunun bir versiyonunu bir pastebin sayfasında buldum ve burada yeniden yayınladım : coliru.stacked-crooked.com/a/de06f2f63f905b7e .
Johannes Schaub -


13

Benim C ++ programım biraz paslı, bu yüzden mükemmel olmayabilir, ama yakın.

template <int N> struct Factorial
{
    enum { val = Factorial<N-1>::val * N };
};

template <> struct Factorial<0>
{
    enum { val = 1 };
}

const int num = Factorial<10>::val;    // num set to 10! at compile time.

Önemli olan, derleyicinin bir yanıta ulaşana kadar özyinelemeli tanımı tamamen değerlendirdiğini göstermektir.


1
Umm ... şablon uzmanlaşmasını belirtmek için Factorial <0> yapısından önceki satırda "şablon <>" olması gerekmez mi?
paxos1977

11

Önemsiz olmayan bir örnek vermek gerekirse: http://gitorious.org/metatrace , bir C ++ derleme zamanı ışın izleyicisi.

C ++ 0x'in şablonsuz, derleme zamanı, turing-tamamlama tesisi şu şekilde ekleyeceğini unutmayın constexpr:

constexpr unsigned int fac (unsigned int u) {
        return (u<=1) ? (1) : (u*fac(u-1));
}

constexpr-İfadeyi derleme zamanı sabitlerine ihtiyaç duyduğunuz her yerde kullanabilirsiniz , ancak constexprconst olmayan parametrelerle -fonksiyonlar da çağırabilirsiniz .

Harika bir şey, bunun sonunda zaman kayan noktalı matematiği derlemeyi mümkün kılacak olmasıdır, ancak standart, zaman kayan noktalı aritmetik derlemenin çalışma zamanı kayan nokta aritmetiği ile eşleşmek zorunda olmadığını açıkça belirtir:

bool f(){
    char array[1+int(1+0.2-0.1-0.1)]; //Must be evaluated during translation
    int  size=1+int(1+0.2-0.1-0.1); //May be evaluated at runtime
    return sizeof(array)==size;
}

F () değerinin doğru mu yanlış mı olacağı belirtilmemiştir.



8

Faktöriyel örnek aslında şablonların İlkel Özyinelemeyi desteklediklerini gösterdiği kadar Turing'in tamamlandığını göstermez. Şablonların tamamlandığını göstermenin en kolay yolu Church-Turing tezidir, yani ya bir Turing makinesi (dağınık ve biraz anlamsız) ya da türlenmemiş lambda hesabının üç kuralını (app, abs var) uygulamaktır. İkincisi çok daha basit ve çok daha ilginç.

Tartışılan şey, C ++ şablonlarının derleme zamanında saf işlevsel programlamaya izin verdiğini anladığınızda son derece yararlı bir özelliktir; ifade edici, güçlü ve zarif ama aynı zamanda az deneyiminiz varsa yazmak için çok karmaşık bir biçimcilik. Ayrıca, kaç kişinin sadece yoğun bir şekilde şablon haline getirilmiş kod almanın genellikle büyük bir çaba gerektirebileceğini düşündüğüne dikkat edin: Bu, derlemeyi zorlaştıran ancak şaşırtıcı bir şekilde hata ayıklama gerektirmeyen kod üreten (saf) işlevsel dillerde durumdur.


Hey, hangi üç kurala atıfta bulunuyorsun, merak ediyorum, "app, abs, var" derken? İlk ikisinin sırasıyla fonksiyon uygulaması ve soyutlama (lambda tanımı (?)) Olduğunu varsayıyorum. Öyle mi? Ve üçüncüsü nedir? Değişkenlerle ilgili bir şey mi var?
Wizek

Ben şahsen, derleyicide bir dilin İlkel Özyinelemeyi desteklemesinin Turing Complete olmaktan daha iyi olacağını düşünüyorum, çünkü derleme zamanı İlkel Özyinelemeyi destekleyen bir dil için derleyici herhangi bir derlemenin tamamlanacağını veya başarısız olacağını garanti edebilir, ancak Yapım süreci Turing Complete olan biri yapıyı yapay olarak kısıtlamak dışında Turing Complete yapamaz.
supercat

5

Sanırım buna şablon meta programlama deniyor .


2
İşin yararlı tarafı budur. Dezavantajı, çoğu insanın (ve kesinlikle benim değil) bu şeylerin çoğunda olanların küçük bir yüzdesini bile anlayacağından şüpheliyim. Korkunç derecede okunamaz, sürdürülemez şeyler.
Michael Burr

3
Sanırım tüm C ++ dilinin dezavantajı bu. Canavar oluyor ...
Federico A. Ramponi

C ++ 0x, çoğunu kolaylaştırmayı vaat ediyor (ve deneyimlerime göre, en büyük sorun, onu tam olarak desteklemeyen derleyicilerdir, C ++ 0x yardımcı olmaz). Özellikle kavramlar, SFINAE'nin okuması zor olan birçok şeyden kurtulmak gibi şeyleri açıklığa kavuşturacak gibi görünüyor.
coppro

@MichaelBurr C ++ komitesi okunamayan, sürdürülemeyen şeyleri umursamıyor; sadece özellik eklemeyi severler .
Sapphire_Brick

4

İşte 4 durumlu 2 sembollü meşgul kunduz çalıştıran bir derleme zamanı Turing Machine uygulaması

#include <iostream>

#pragma mark - Tape

constexpr int Blank = -1;

template<int... xs>
class Tape {
public:
    using type = Tape<xs...>;
    constexpr static int length = sizeof...(xs);
};

#pragma mark - Print

template<class T>
void print(T);

template<>
void print(Tape<>) {
    std::cout << std::endl;
}

template<int x, int... xs>
void print(Tape<x, xs...>) {
    if (x == Blank) {
        std::cout << "_ ";
    } else {
        std::cout << x << " ";
    }
    print(Tape<xs...>());
}

#pragma mark - Concatenate

template<class, class>
class Concatenate;

template<int... xs, int... ys>
class Concatenate<Tape<xs...>, Tape<ys...>> {
public:
    using type = Tape<xs..., ys...>;
};

#pragma mark - Invert

template<class>
class Invert;

template<>
class Invert<Tape<>> {
public:
    using type = Tape<>;
};

template<int x, int... xs>
class Invert<Tape<x, xs...>> {
public:
    using type = typename Concatenate<
        typename Invert<Tape<xs...>>::type,
        Tape<x>
    >::type;
};

#pragma mark - Read

template<int, class>
class Read;

template<int n, int x, int... xs>
class Read<n, Tape<x, xs...>> {
public:
    using type = typename std::conditional<
        (n == 0),
        std::integral_constant<int, x>,
        Read<n - 1, Tape<xs...>>
    >::type::type;
};

#pragma mark - N first and N last

template<int, class>
class NLast;

template<int n, int x, int... xs>
class NLast<n, Tape<x, xs...>> {
public:
    using type = typename std::conditional<
        (n == sizeof...(xs)),
        Tape<xs...>,
        NLast<n, Tape<xs...>>
    >::type::type;
};

template<int, class>
class NFirst;

template<int n, int... xs>
class NFirst<n, Tape<xs...>> {
public:
    using type = typename Invert<
        typename NLast<
            n, typename Invert<Tape<xs...>>::type
        >::type
    >::type;
};

#pragma mark - Write

template<int, int, class>
class Write;

template<int pos, int x, int... xs>
class Write<pos, x, Tape<xs...>> {
public:
    using type = typename Concatenate<
        typename Concatenate<
            typename NFirst<pos, Tape<xs...>>::type,
            Tape<x>
        >::type,
        typename NLast<(sizeof...(xs) - pos - 1), Tape<xs...>>::type
    >::type;
};

#pragma mark - Move

template<int, class>
class Hold;

template<int pos, int... xs>
class Hold<pos, Tape<xs...>> {
public:
    constexpr static int position = pos;
    using tape = Tape<xs...>;
};

template<int, class>
class Left;

template<int pos, int... xs>
class Left<pos, Tape<xs...>> {
public:
    constexpr static int position = typename std::conditional<
        (pos > 0),
        std::integral_constant<int, pos - 1>,
        std::integral_constant<int, 0>
    >::type();

    using tape = typename std::conditional<
        (pos > 0),
        Tape<xs...>,
        Tape<Blank, xs...>
    >::type;
};

template<int, class>
class Right;

template<int pos, int... xs>
class Right<pos, Tape<xs...>> {
public:
    constexpr static int position = pos + 1;

    using tape = typename std::conditional<
        (pos < sizeof...(xs) - 1),
        Tape<xs...>,
        Tape<xs..., Blank>
    >::type;
};

#pragma mark - States

template <int>
class Stop {
public:
    constexpr static int write = -1;
    template<int pos, class tape> using move = Hold<pos, tape>;
    template<int x> using next = Stop<x>;
};

#define ADD_STATE(_state_)      \
template<int>                   \
class _state_ { };

#define ADD_RULE(_state_, _read_, _write_, _move_, _next_)          \
template<>                                                          \
class _state_<_read_> {                                             \
public:                                                             \
    constexpr static int write = _write_;                           \
    template<int pos, class tape> using move = _move_<pos, tape>;   \
    template<int x> using next = _next_<x>;                         \
};

#pragma mark - Machine

template<template<int> class, int, class>
class Machine;

template<template<int> class State, int pos, int... xs>
class Machine<State, pos, Tape<xs...>> {
    constexpr static int symbol = typename Read<pos, Tape<xs...>>::type();
    using state = State<symbol>;

    template<int x>
    using nextState = typename State<symbol>::template next<x>;

    using modifiedTape = typename Write<pos, state::write, Tape<xs...>>::type;
    using move = typename state::template move<pos, modifiedTape>;

    constexpr static int nextPos = move::position;
    using nextTape = typename move::tape;

public:
    using step = Machine<nextState, nextPos, nextTape>;
};

#pragma mark - Run

template<class>
class Run;

template<template<int> class State, int pos, int... xs>
class Run<Machine<State, pos, Tape<xs...>>> {
    using step = typename Machine<State, pos, Tape<xs...>>::step;

public:
    using type = typename std::conditional<
        std::is_same<State<0>, Stop<0>>::value,
        Tape<xs...>,
        Run<step>
    >::type::type;
};

ADD_STATE(A);
ADD_STATE(B);
ADD_STATE(C);
ADD_STATE(D);

ADD_RULE(A, Blank, 1, Right, B);
ADD_RULE(A, 1, 1, Left, B);

ADD_RULE(B, Blank, 1, Left, A);
ADD_RULE(B, 1, Blank, Left, C);

ADD_RULE(C, Blank, 1, Right, Stop);
ADD_RULE(C, 1, 1, Left, D);

ADD_RULE(D, Blank, 1, Right, D);
ADD_RULE(D, 1, Blank, Right, A);

using tape = Tape<Blank>;
using machine = Machine<A, 0, tape>;
using result = Run<machine>::type;

int main() {
    print(result());
    return 0;
}

İdeon prova çalışması: https://ideone.com/MvBU3Z

Açıklama: http://victorkomarov.blogspot.ru/2016/03/compile-time-turing-machine.html

Daha fazla örnek içeren Github: https://github.com/fnz/CTTM


3

O kadar da önemsiz olmadığını düşündüğüm şablonlarla FFT uygulaması hakkında Dr. Dobbs'un bu makalesine göz atabilirsiniz. Temel nokta, derleyicinin şablon olmayan uygulamalardan daha iyi bir optimizasyon gerçekleştirmesine izin vermektir çünkü FFT algoritması çok sayıda sabit kullanır (örneğin sin tabloları)

bölüm I

bölüm II


2

Hata ayıklaması neredeyse imkansız olsa da, tamamen işlevsel bir dil olduğunu belirtmek de eğlenceli. James gönderisine bakarsanız, işlevsel olmakla ne demek istediğimi göreceksiniz. Genel olarak C ++ 'nın en kullanışlı özelliği değildir. Bunu yapmak için tasarlanmadı. Keşfedilen bir şey.



1

Makul derecede kullanışlı bir örnek, bir oran sınıfıdır. Etrafta yüzen birkaç varyant var. D == 0 durumunu yakalamak, kısmi aşırı yüklenmelerle oldukça basittir. Gerçek hesaplama, N ve D'nin OBEB ve derleme zamanını hesaplamaktır. Derleme zamanı hesaplamalarında bu oranları kullandığınızda bu çok önemlidir.

Örnek: Santimetre (5) * kilometre (5) hesaplarken, derleme zamanında <1.100> oranı ve <1000,1> oranını çarpacaksınız. Taşmayı önlemek için <1000,100> oranı yerine <10,1> oranını istiyorsunuz.


0

Bir Turing makinesi Turing-tamamlanmıştır, ancak bu, üretim kodu için bir tane kullanmak istemeniz gerektiği anlamına gelmez.

Şablonlarla önemsiz olmayan bir şey yapmaya çalışmak benim deneyimime göre dağınık, çirkin ve anlamsız. "Kodunuzda" "hata ayıklama" yapmanın bir yolu yoktur, derleme zamanı hata mesajları şifreli olacaktır ve genellikle en olası olmayan yerlerde ve aynı performans avantajlarını farklı şekillerde elde edebilirsiniz. (İpucu: 4! = 24). Daha da kötüsü, kodunuz ortalama bir C ++ programcısı tarafından anlaşılmaz ve mevcut derleyicilerdeki çok çeşitli destek seviyeleri nedeniyle muhtemelen taşınabilir olmayacaktır.

Şablonlar genel kod üretimi için harikadır (kapsayıcı sınıfları, sınıf paketleyicileri, karışımlar), ancak hayır - bence şablonların Turing Tamlığı pratikte YARARLI DEĞİL .


4! 24 olabilir, ancak MY_FAVORITE_MACRO_VALUE nedir! ? Tamam, aslında bunun da iyi bir fikir olduğunu düşünmüyorum.
Jeffrey L Whitledge

0

Nasıl programlanmayacağına dair bir başka örnek:

şablon <int Derinlik, int A, tür adı B>
struct K17 {
    statik sabit int x =
    K17 <Derinlik + 1, 0, K17 <Derinlik, A, B>> :: x
    + K17 <Derinlik + 1, 1, K17 <Derinlik, A, B>> :: x
    + K17 <Derinlik + 1, 2, K17 <Derinlik, A, B>> :: x
    + K17 <Derinlik + 1, 3, K17 <Derinlik, A, B>> :: x
    + K17 <Derinlik + 1, 4, K17 <Derinlik, A, B>> :: x;
};
şablon <int A, tür adı B>
yapı K17 <16, A, B> {statik sabit int x = 1; };
statik sabit int z = K17 <0,0, int> :: x;
void main (void) {}

Biz Post C ++ şablonları tam turing edilir


merak edenler için, x'in cevabı pow (5,17-derinlik);
flownt

A ve B şablon bağımsız değişkenlerinin hiçbir şey yapmadığını ve bunları silip tüm eklemeyi ile değiştirdiğini fark ettiğinizde bunu görmek çok daha kolaydır K17<Depth+1>::x * 5.
David Stone
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.