C ++ template Turing-complete?

111

Saya diberitahu bahwa sistem template di C ++ adalah Turing-complete pada waktu kompilasi. Ini disebutkan dalam posting ini dan juga di wikipedia .

Dapatkah Anda memberikan contoh komputasi yang tidak sepele yang mengeksploitasi properti ini?

Apakah fakta ini berguna dalam praktiknya?

Federico A. Ramponi
sumber

Jawaban:

110

Contoh

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

Itu sedikit menyenangkan tapi tidak terlalu praktis.

Untuk menjawab bagian kedua dari pertanyaan:
Apakah fakta ini berguna dalam praktiknya?

Jawaban Singkat: Semacam.

Jawaban Panjang: Ya, tetapi hanya jika Anda adalah daemon template.

Untuk menghasilkan pemrograman yang bagus dengan menggunakan template meta-pemrograman yang benar-benar berguna untuk digunakan orang lain (yaitu perpustakaan) benar-benar sangat sulit (meskipun dapat dilakukan). Untuk Membantu meningkatkan bahkan memiliki MPL alias (Meta Programming Library). Tapi coba debugging kesalahan kompiler dalam kode template Anda dan Anda akan berada dalam perjalanan panjang yang sulit.

Tetapi contoh praktis yang baik tentang itu digunakan untuk sesuatu yang bermanfaat:

Scott Meyers telah bekerja ekstensi ke bahasa C ++ (saya menggunakan istilah longgar) menggunakan fasilitas template. Anda dapat membaca tentang pekerjaannya di sini ' Fitur Penegakan Kode '

Martin York
sumber
36
Dang di sana pergi konsep (poof)
Martin York
5
Saya hanya memiliki masalah kecil dengan contoh yang diberikan - ini tidak mengeksploitasi (penuh) kelengkapan Turing dari sistem template C ++. Faktorial dapat ditemukan juga menggunakan fungsi rekursif Primitif, yang tidak lengkap turing
Dalibor Frivaldsky
4
dan sekarang kami memiliki konsep lite
nurettin
1
Selama tahun 2017, kami mendorong konsep lebih jauh ke belakang. Inilah harapan untuk tahun 2020.
DeiDei
2
@ MarkKegel 12 tahun kemudian: D
Victor
181

Saya telah melakukan mesin turing di C ++ 11. Fitur yang ditambahkan C ++ 11 memang tidak signifikan untuk mesin turing. Ini hanya menyediakan untuk daftar aturan panjang sewenang-wenang menggunakan templat variadic, daripada menggunakan metaprogramming makro yang sesat :). Nama untuk kondisi digunakan untuk menampilkan diagram di stdout. Saya telah menghapus kode itu agar sampelnya tetap pendek.

#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!");
}
Johannes Schaub - litb
sumber
131
Anda memiliki terlalu banyak waktu luang.
Mark Kegel
2
Ini terlihat seperti cadel kecuali dengan kata certin yang menggantikan semua tanda kurung tersebut.
Simon Kuang
1
Apakah sumber lengkapnya tersedia untuk umum di suatu tempat, untuk pembaca yang penasaran? :)
OJFord
1
Hanya upaya yang pantas mendapatkan kredit lebih banyak :-) Kode ini mengkompilasi (gcc-4.9) tetapi tidak memberikan keluaran - sedikit informasi lagi, seperti posting blog, akan bagus.
Alfred Bratterud
2
@OllieFord Saya menemukan versinya di halaman pastebin dan memposting ulang di sini: coliru.stacked-crooked.com/a/de06f2f63f905b7e .
Johannes Schaub - litb
13

C ++ saya agak berkarat, jadi mungkin tidak sempurna, tapi hampir saja.

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.

Intinya adalah untuk menunjukkan bahwa kompilator mengevaluasi sepenuhnya definisi rekursif hingga mencapai jawaban.

James Curran
sumber
1
Umm ... tidakkah Anda perlu memiliki "template <>" pada baris sebelum struct Factorial <0> untuk menunjukkan spesialisasi template?
paxos1977
11

Untuk memberikan contoh yang tidak sepele: http://gitorious.org/metatrace , C ++ compile time ray tracer.

Perhatikan bahwa C ++ 0x akan menambahkan fasilitas non-template, compile-time, turing-complete berupa constexpr:

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

Anda dapat menggunakan constexpr-expression di mana pun Anda membutuhkan kompilasi konstanta waktu, tetapi Anda juga dapat memanggil constexpr-functions dengan parameter non-const.

Satu hal yang keren adalah bahwa ini akhirnya akan memungkinkan waktu kompilasi matematika floating point, meskipun standar secara eksplisit menyatakan bahwa waktu kompilasi aritmatika floating point tidak harus cocok dengan runtime aritmatika floating point:

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

Tidak ditentukan apakah nilai f () akan benar atau salah.

Sebastian Mach
sumber
8

Contoh faktorial sebenarnya tidak menunjukkan bahwa template sudah lengkap Turing, melainkan menunjukkan bahwa template tersebut mendukung Primitive Recursion. Cara termudah untuk menunjukkan bahwa templat sudah lengkap adalah dengan tesis Church-Turing, yaitu dengan mengimplementasikan mesin Turing (berantakan dan sedikit tidak berguna) atau tiga aturan (app, abs var) dari kalkulus lambda tanpa tipe. Yang terakhir jauh lebih sederhana dan jauh lebih menarik.

Apa yang didiskusikan adalah fitur yang sangat berguna ketika Anda memahami bahwa template C ++ memungkinkan pemrograman fungsional murni pada waktu kompilasi, formalisme yang ekspresif, kuat dan elegan tetapi juga sangat rumit untuk ditulis jika Anda memiliki sedikit pengalaman. Perhatikan juga berapa banyak orang yang menemukan bahwa mendapatkan kode yang sangat bertemplatasi sering kali memerlukan usaha yang besar: hal ini persis terjadi pada bahasa fungsional (murni), yang membuat kompilasi lebih sulit tetapi secara mengejutkan menghasilkan kode yang tidak memerlukan debugging.


sumber
Hei, apa tiga aturan yang Anda rujuk, saya ingin tahu, dengan "app, abs, var"? Saya menganggap dua yang pertama adalah aplikasi fungsi dan abstraksi (definisi lambda (?)) Masing-masing. Apakah begitu? Dan apakah yang ketiga? Ada hubungannya dengan variabel?
Wizek
Saya pribadi berpikir bahwa secara umum akan lebih baik menggunakan bahasa yang mendukung Primitive Recursion dalam kompilator daripada memilikinya Turing Complete, karena kompilator untuk bahasa yang mendukung Primitive Recursion waktu kompilasi dapat menjamin bahwa setiap build akan selesai atau gagal, tetapi orang yang proses buildnya Turing Complete tidak bisa, kecuali dengan membatasi build secara artifisial sehingga bukan Turing Complete.
supercat
5

Saya pikir itu disebut meta-programming template .

Tom Ritter
sumber
2
Ini adalah sisi yang berguna. Sisi negatifnya adalah bahwa saya ragu kebanyakan orang (dan tentu saja bukan saya) akan benar-benar memahami bahkan sebagian kecil dari apa yang terjadi di sebagian besar hal itu. Ini hal yang sangat tidak bisa dibaca, tidak bisa dirawat.
Michael Burr
3
Itulah kekurangan dari keseluruhan bahasa C ++, saya kira. Ini menjadi monster ...
Federico A. Ramponi
C ++ 0x menjanjikan untuk membuatnya lebih mudah (dan menurut pengalaman saya, masalah terbesar adalah kompiler yang tidak mendukungnya sepenuhnya, yang tidak akan membantu C ++ 0x). Konsep khususnya terlihat seperti mereka akan menjernihkan banyak hal, seperti membuang banyak hal SFINAE, yang sulit dibaca.
coppro
@MichaelBurr Komite C ++ tidak peduli tentang hal-hal yang tidak dapat dibaca dan tidak dapat dirawat; mereka suka menambahkan fitur.
Sapphire_Brick
4

Nah, berikut ini adalah waktu kompilasi implementasi Turing Machine yang menjalankan berang-berang sibuk dengan 2 simbol 4-status

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

Uji coba Ideone: https://ideone.com/MvBU3Z

Penjelasan: http://victorkomarov.blogspot.ru/2016/03/compile-time-turing-machine.html

Github dengan lebih banyak contoh: https://github.com/fnz/CTTM

Victor Komarov
sumber
3

Anda dapat memeriksa artikel ini dari Dr. Dobbs tentang implementasi FFT dengan template yang menurut saya tidak sepele. Poin utamanya adalah memungkinkan kompiler untuk melakukan optimasi yang lebih baik daripada implementasi non template karena algoritma FFT menggunakan banyak konstanta (misalnya tabel sin)

bagian I

Bagian II


sumber
2

Ini juga menyenangkan untuk menunjukkan bahwa ini adalah bahasa yang berfungsi murni meskipun hampir tidak mungkin untuk di-debug. Jika Anda melihat posting James Anda akan melihat apa yang saya maksud dengan berfungsi. Secara umum, ini bukan fitur C ++ yang paling berguna. Itu tidak dirancang untuk melakukan ini. Itu adalah sesuatu yang ditemukan.

Harga Matt
sumber
2

Mungkin berguna jika Anda ingin menghitung konstanta pada waktu kompilasi, setidaknya dalam teori. Lihat pemrograman metaprogram .


sumber
1

Contoh yang cukup berguna adalah kelas rasio. Ada beberapa varian yang beredar. Menangkap kasus D == 0 cukup sederhana dengan sebagian kelebihan beban. Komputasi sebenarnya adalah menghitung GCD dari N dan D dan waktu kompilasi. Ini penting saat Anda menggunakan rasio ini dalam penghitungan waktu kompilasi.

Contoh: Saat Anda menghitung sentimeter (5) * kilometer (5), pada waktu kompilasi Anda akan mengalikan rasio <1.100> dan rasio <1000,1>. Untuk mencegah overflow, Anda menginginkan rasio <10,1> daripada rasio <1000.100>.

MSalters
sumber
0

Sebuah mesin Turing adalah Turing-lengkap, tapi itu tidak berarti Anda harus ingin menggunakan satu untuk kode produksi.

Mencoba melakukan sesuatu yang tidak sepele dengan templat menurut pengalaman saya berantakan, jelek, dan tidak berguna. Anda tidak memiliki cara untuk "men-debug" "kode" Anda, pesan kesalahan waktu kompilasi akan menjadi samar dan biasanya di tempat yang paling tidak mungkin, dan Anda dapat memperoleh manfaat kinerja yang sama dengan cara yang berbeda. (Petunjuk: 4! = 24). Lebih buruk lagi, kode Anda tidak dapat dipahami oleh programmer C ++ rata-rata, dan kemungkinan besar akan non-portabel karena tingkat dukungan yang luas dalam kompiler saat ini.

Template sangat bagus untuk pembuatan kode generik (kelas kontainer, pembungkus kelas, campuran), tetapi tidak - menurut saya Turing Completeness of templates TIDAK BERGUNA dalam praktiknya.

Roddy
sumber
4! mungkin 24, tapi apa MY_FAVORITE_MACRO_VALUE! ? Oke, menurutku ini juga bukan ide yang bagus.
Jeffrey L Whitledge
0

Contoh lain bagaimana tidak memprogram:

template <int Depth, int A, typename B>
struct K17 {
    statis const int x =
    K17 <Kedalaman + 1, 0, K17 <Kedalaman, A, B>> :: x
    + K17 <Kedalaman + 1, 1, K17 <Kedalaman, A, B>> :: x
    + K17 <Kedalaman + 1, 2, K17 <Kedalaman, A, B>> :: x
    + K17 <Kedalaman + 1, 3, K17 <Kedalaman, A, B>> :: x
    + K17 <Kedalaman + 1, 4, K17 <Kedalaman, A, B>> :: x;
};
template <int A, ketik nama B>
struct K17 <16, A, B> {static const int x = 1; };
statis const int z = K17 <0,0, int> :: x;
batal utama (batal) {}

Posting di C ++ template sudah selesai

lsalamon
sumber
Bagi yang penasaran, jawaban untuk x adalah pow (kedalaman 5,17);
diterbangkan
Yang jauh lebih sederhana untuk dilihat saat Anda menyadari bahwa argumen templat A dan B tidak melakukan apa-apa dan menghapusnya, lalu mengganti semua penambahan dengan K17<Depth+1>::x * 5.
David Stone