แม่แบบ C ++ ทัวริงสมบูรณ์?


111

ฉันบอกว่าระบบเทมเพลตใน C ++ ทัวริงสมบูรณ์ในเวลาคอมไพล์ สิ่งนี้ถูกกล่าวถึงในโพสต์นี้และในวิกิพีเดีย

คุณสามารถให้ตัวอย่างการคำนวณที่ใช้ประโยชน์จากคุณสมบัตินี้ได้หรือไม่

ข้อเท็จจริงนี้มีประโยชน์ในทางปฏิบัติหรือไม่?

คำตอบ:


110

ตัวอย่าง

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

นั่นเป็นเรื่องสนุกเล็กน้อย แต่ไม่สามารถใช้ได้จริง

เพื่อตอบคำถามส่วนที่สอง:
ข้อเท็จจริงนี้มีประโยชน์ในทางปฏิบัติหรือไม่?

คำตอบสั้น ๆ : เรียงลำดับจาก.

คำตอบแบบยาว: ใช่ แต่ถ้าคุณเป็นดีมอนเทมเพลตเท่านั้น

การเขียนโปรแกรมที่ดีโดยใช้การเขียนโปรแกรมแบบเมตาดาต้าแม่แบบที่มีประโยชน์มากสำหรับผู้อื่นในการใช้งาน (เช่นไลบรารี) นั้นยากจริงๆ (แม้ว่าจะทำได้) หากต้องการช่วยเพิ่มยังมีMPL aka (Meta Programming Library) แต่ลองแก้ไขข้อผิดพลาดของคอมไพเลอร์ในรหัสเทมเพลตของคุณแล้วคุณจะต้องนั่งรถต่อไปอีกนาน

แต่ตัวอย่างในทางปฏิบัติที่ดีของการนำไปใช้เพื่อประโยชน์:

Scott Meyers ใช้ส่วนขยายของภาษา C ++ (ฉันใช้คำนี้แบบหลวม ๆ ) โดยใช้สิ่งอำนวยความสะดวกในการสร้างเทมเพลต คุณสามารถอ่านเกี่ยวกับผลงานของเขาได้ที่นี่ ' Enforcing Code Features '


36
Dang มีแนวคิด (poof)
Martin York

5
ฉันมีปัญหาเล็กน้อยกับตัวอย่างที่ให้มา - มันไม่ได้ใช้ประโยชน์จากระบบเทมเพลตของ C ++ (แบบเต็ม) ทัวริง สามารถพบ Factorial ได้โดยใช้ฟังก์ชันการเรียกซ้ำแบบดั้งเดิมซึ่งไม่สมบูรณ์
Dalibor Frivaldsky

4
และตอนนี้เรามีแนวคิด lite
nurettin

1
ในปี 2560 เรากำลังผลักดันแนวคิดให้ก้าวหน้ายิ่งขึ้นไปอีก นี่คือความหวังสำหรับปี 2020
DeiDei

2
@MarkKegel 12 ปีต่อมา: D
Victor

181

ฉันได้ทำเครื่องทัวริงใน C ++ 11 แล้ว คุณสมบัติที่ C ++ 11 เพิ่มนั้นไม่สำคัญสำหรับเครื่องทัวริงแน่นอน เพียงแค่จัดเตรียมรายการกฎความยาวโดยพลการโดยใช้เทมเพลตตัวแปรแทนที่จะใช้การเขียนโปรแกรมมาโครที่ผิดเพี้ยน :) ชื่อของเงื่อนไขถูกใช้เพื่อส่งออกไดอะแกรมบน stdout ฉันได้ลบรหัสนั้นออกเพื่อให้เป็นตัวอย่างสั้น ๆ

#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
คุณมีเวลาในมือมากเกินไป
Mark Kegel

2
ดูเหมือนเสียงกระเพื่อมยกเว้นด้วยคำรับรองแทนที่วงเล็บทั้งหมด
Simon Kuang

1
แหล่งข้อมูลฉบับเต็มมีให้บริการแบบสาธารณะสำหรับผู้อ่านที่อยากรู้อยากเห็นหรือไม่? :)
OJFord

1
เพียงแค่ความพยายามก็สมควรได้รับเครดิตมากขึ้น :-) โค้ดนี้คอมไพล์ (gcc-4.9) แต่ไม่มีผลลัพธ์ - ข้อมูลเพิ่มเติมเล็กน้อยเช่นบล็อกโพสต์จะดีมาก
Alfred Bratterud

2
@OllieFord ผมพบว่ารุ่นของมันบนหน้า Pastebin ที่และ repasted ได้ที่นี่: coliru.stacked-crooked.com/a/de06f2f63f905b7e
Johannes Schaub - litb

31

" C ++ Templates Are Turing Complete " นำเสนอการใช้งานเครื่องทัวริงในเทมเพลต ... ซึ่งไม่สำคัญและพิสูจน์ประเด็นได้อย่างตรงจุด แน่นอนว่ามันไม่มีประโยชน์มากนัก!


4
ลิงค์ที่ดียิ่งขึ้น: turing machine ใน c ++ 1x
Martin York

13

C ++ ของฉันค่อนข้างเป็นสนิมดังนั้นอาจไม่สมบูรณ์แบบ แต่ก็ใกล้เคียง

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.

ประเด็นคือการแสดงให้เห็นว่าคอมไพเลอร์กำลังประเมินนิยามการเรียกซ้ำอย่างสมบูรณ์จนกว่าจะได้คำตอบ


1
อืม ... คุณไม่จำเป็นต้องมี "template <>" บนบรรทัดก่อน struct Factorial <0> เพื่อระบุความเชี่ยวชาญของเทมเพลตหรือไม่?
paxos1977

11

หากต้องการให้ตัวอย่างที่ไม่สำคัญ: http://gitorious.org/metatraceซึ่งเป็น C ++ ที่รวบรวมตัวติดตามเรย์เวลา

โปรดทราบว่า C ++ 0x จะเพิ่มสิ่งอำนวยความสะดวกที่ไม่ใช่เทมเพลตเวลาคอมไพล์และทัวริงในรูปแบบของconstexpr:

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

คุณสามารถใช้constexpr-expression ได้ทุกที่ที่คุณต้องการค่าคงที่ของเวลาในการคอมไพล์ แต่คุณยังสามารถเรียกใช้constexprฟังก์ชันที่มีพารามิเตอร์ที่ไม่ใช่ const ได้

สิ่งที่ยอดเยี่ยมอย่างหนึ่งคือในที่สุดสิ่งนี้จะเปิดใช้งานการคำนวณเลขทศนิยมของเวลาในที่สุดแม้ว่ามาตรฐานจะระบุไว้อย่างชัดเจนว่าการรวบรวมเลขคณิตจุดลอยตัวของเวลาไม่จำเป็นต้องตรงกับเลขคณิตทศนิยมของรันไทม์:

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 () จะเป็นจริงหรือเท็จ


8

การออกแบบ C ++ สมัยใหม่ของหนังสือ- รูปแบบการเขียนโปรแกรมและการออกแบบทั่วไปโดย Andrei Alexandrescu เป็นสถานที่ที่ดีที่สุดในการสัมผัสประสบการณ์ด้วยรูปแบบการเขียนโปรแกรมทั่วไปที่มีประโยชน์และมีประสิทธิภาพ


8

ตัวอย่างแฟคทอเรียลไม่ได้แสดงให้เห็นว่าเทมเพลตนั้นเป็นแบบทัวริงที่สมบูรณ์เท่าที่จะแสดงให้เห็นว่ามันรองรับ Primitive Recursion วิธีที่ง่ายที่สุดในการแสดงให้เห็นว่าแม่แบบนั้นสมบูรณ์คือวิทยานิพนธ์ของ Church-Turing นั่นคือการใช้เครื่องทัวริง (ยุ่งและไม่มีจุดหมายเล็กน้อย) หรือกฎสามข้อ (app, abs var) ของแคลคูลัสแลมบ์ดาที่ไม่ได้พิมพ์ อย่างหลังง่ายกว่าและน่าสนใจกว่ามาก

สิ่งที่กำลังพูดถึงคือคุณสมบัติที่มีประโยชน์อย่างยิ่งเมื่อคุณเข้าใจว่าเทมเพลต C ++ ช่วยให้สามารถเขียนโปรแกรมที่ใช้งานได้จริงในเวลาคอมไพล์เป็นพิธีการที่แสดงออกมีประสิทธิภาพและสง่างาม แต่ยังซับซ้อนมากในการเขียนหากคุณมีประสบการณ์น้อย สังเกตด้วยว่ามีกี่คนที่พบว่าการได้รับโค้ดเทมเพลตอย่างหนักมักต้องใช้ความพยายามอย่างมาก: นี่เป็นกรณีของภาษาที่ใช้งานได้ (บริสุทธิ์) ซึ่งทำให้การคอมไพล์โค้ดยากขึ้น แต่น่าประหลาดใจที่ไม่จำเป็นต้องมีการดีบั๊ก


เฮ้คุณอ้างถึงกฎสามข้ออะไรฉันสงสัยว่าโดย "app, abs, var"? ฉันคิดว่าสองตัวแรกคือแอปพลิเคชันฟังก์ชันและนามธรรม (นิยามแลมบ์ดา (?)) ตามลำดับ ขนาดนั้นเลยเหรอ แล้วอันที่สามล่ะ? มีอะไรเกี่ยวข้องกับตัวแปร?
Wizek

โดยส่วนตัวแล้วฉันคิดว่าโดยทั่วไปแล้วการสนับสนุนภาษา Primitive Recursion ในคอมไพเลอร์จะดีกว่าการเป็น Turing Complete เนื่องจากคอมไพเลอร์สำหรับภาษาที่รองรับ Primitive Recursion แบบ compile-time สามารถรับประกันได้ว่า build ใด ๆ จะเสร็จสมบูรณ์หรือล้มเหลว แต่ ผู้ที่กระบวนการสร้างเป็น Turing Complete ไม่สามารถทำได้ยกเว้นโดยการ จำกัด การสร้างแบบเทียมดังนั้นจึงไม่ Turing Complete
supercat

5

ฉันคิดว่ามันเรียกว่าแม่แบบ meta-การเขียนโปรแกรม


2
นี่คือด้านที่มีประโยชน์ของมัน ข้อเสียคือฉันสงสัยว่าคนส่วนใหญ่ (และไม่ใช่ฉันอย่างแน่นอน) จะเข้าใจแม้เพียงเล็กน้อยว่าเกิดอะไรขึ้นในส่วนใหญ่ เป็นสิ่งที่อ่านไม่ออกและไม่สามารถเข้าถึงได้อย่างน่าสยดสยอง
Michael Burr

3
นั่นเป็นข้อเสียเปรียบของภาษา C ++ ทั้งหมดฉันคิดว่า มันกลายเป็นสัตว์ประหลาด ...
Federico A.Ramponi

C ++ 0x สัญญาว่าจะทำให้ง่ายขึ้นมาก (และจากประสบการณ์ของฉันปัญหาใหญ่ที่สุดคือคอมไพเลอร์ที่ไม่รองรับอย่างเต็มที่ซึ่ง C ++ 0x จะไม่ช่วย) แนวคิดโดยเฉพาะดูเหมือนว่าพวกเขาจะเคลียร์สิ่งต่างๆเช่นการกำจัดสิ่ง SFINAE จำนวนมากซึ่งอ่านยาก
coppro

@MichaelBurr คณะกรรมการ C ++ ไม่สนใจเกี่ยวกับสิ่งที่อ่านไม่ได้และไม่สามารถเข้าถึงได้ พวกเขาชอบที่จะเพิ่มคุณสมบัติ
Sapphire_Brick

4

ต่อไปนี้เป็นเวลาคอมไพล์การใช้งาน Turing Machine ที่รันบีเวอร์ 4 สถานะ 2 สัญลักษณ์ไม่ว่าง

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

การพิสูจน์ Ideone: https://ideone.com/MvBU3Z

คำอธิบาย: http://victorkomarov.blogspot.ru/2016/03/compile-time-turing-machine.html

Github พร้อมตัวอย่างเพิ่มเติม: https://github.com/fnz/CTTM


3

คุณสามารถตรวจสอบบทความนี้ได้จาก Dr. Dobbs เกี่ยวกับการใช้งาน FFT กับเทมเพลตซึ่งฉันคิดว่าไม่สำคัญ ประเด็นหลักคือเพื่อให้คอมไพลเลอร์ทำการเพิ่มประสิทธิภาพได้ดีกว่าการใช้งานแบบไม่ใช้เทมเพลตเนื่องจากอัลกอริทึม FFT ใช้ค่าคงที่จำนวนมาก (เช่นตาราง sin)

ส่วน I

ส่วน II


2

นอกจากนี้ยังสนุกที่จะชี้ให้เห็นว่าเป็นภาษาที่ใช้งานได้จริงแม้ว่าแทบจะเป็นไปไม่ได้ที่จะแก้ไขข้อบกพร่อง ถ้าคุณดูโพสต์ของเจมส์คุณจะเห็นว่ามันใช้งานได้จริง โดยทั่วไปไม่ใช่คุณสมบัติที่มีประโยชน์ที่สุดของ C ++ ไม่ได้ออกแบบมาเพื่อทำสิ่งนี้ มันเป็นสิ่งที่ค้นพบ


2

อาจเป็นประโยชน์หากคุณต้องการคำนวณค่าคงที่ในเวลาคอมไพล์อย่างน้อยก็ในทางทฤษฎี ตรวจสอบแม่แบบ metaprogramming


1

ตัวอย่างที่มีประโยชน์พอสมควรคือคลาสอัตราส่วน มีไม่กี่สายพันธุ์ลอยไปมา การจับเคส D == 0 นั้นค่อนข้างง่ายโดยมีการโอเวอร์โหลดบางส่วน การคำนวณที่แท้จริงคือการคำนวณ GCD ของ N และ D และเวลาในการรวบรวม นี่เป็นสิ่งสำคัญเมื่อคุณใช้อัตราส่วนเหล่านี้ในการคำนวณเวลาคอมไพล์

ตัวอย่าง: เมื่อคุณคำนวณเซนติเมตร (5) * กิโลเมตร (5) เวลารวบรวมคุณจะได้อัตราส่วน <1,100> และอัตราส่วน <1,000,1> เพื่อป้องกันการล้นคุณต้องมีอัตราส่วน <10,1> แทนที่จะเป็นอัตราส่วน <1,000,100>


0

เครื่องทัวริงเป็นทัวริงสมบูรณ์ แต่ไม่ได้หมายความว่าคุณควรต้องการที่จะใช้อย่างใดอย่างหนึ่งสำหรับรหัสการผลิต

การพยายามทำสิ่งที่ไม่สำคัญด้วยเทมเพลตนั้นเป็นประสบการณ์ของฉันที่ยุ่งเหยิงน่าเกลียดและไร้จุดหมาย คุณไม่มีทาง "ดีบัก" "โค้ด" ของคุณได้ข้อความแสดงข้อผิดพลาดเวลาคอมไพล์จะเป็นความลับและโดยปกติจะอยู่ในตำแหน่งที่ไม่น่าจะเกิดขึ้นได้มากที่สุดและคุณจะได้รับประโยชน์ด้านประสิทธิภาพเดียวกันในรูปแบบต่างๆ (คำแนะนำ: 4! = 24) ที่แย่กว่านั้นคือโค้ดของคุณไม่สามารถเข้าใจได้สำหรับโปรแกรมเมอร์ C ++ โดยเฉลี่ยและมีแนวโน้มว่าจะไม่สามารถพกพาได้เนื่องจากมีการรองรับหลายระดับภายในคอมไพเลอร์ปัจจุบัน

เทมเพลตนั้นยอดเยี่ยมสำหรับการสร้างโค้ดทั่วไป (คลาสคอนเทนเนอร์, คลาสแรปเปอร์, มิกซ์อิน) แต่ไม่ - ในความคิดของฉันความสมบูรณ์ของเท็มเพลตไม่เป็นประโยชน์ในทางปฏิบัติ


4! อาจจะอายุ 24 แต่ MY_FAVORITE_MACRO_VALUE คืออะไร! เหรอ? โอเคฉันไม่คิดว่านี่เป็นความคิดที่ดีเช่นกัน
Jeffrey L Whitledge

0

อีกตัวอย่างหนึ่งของการไม่ตั้งโปรแกรม:

เทมเพลต <int Depth, int A, typename B>
โครงสร้าง K17 {
    คงที่ const int x =
    K17 <ความลึก + 1, 0, K17 <ความลึก, A, B>> :: x
    + K17 <ความลึก + 1, 1, K17 <ความลึก, A, B>> :: x
    + K17 <ความลึก + 1, 2, K17 <ความลึก, A, B>> :: x
    + K17 <ความลึก + 1, 3, K17 <ความลึก, A, B>> :: x
    + K17 <ความลึก + 1, 4, K17 <ความลึก, A, B>> :: x;
};
เทมเพลต <int A, ชื่อพิมพ์ B>
โครงสร้าง K17 <16, A, B> {คงที่ const int x = 1; };
คงที่ const int z = K17 <0,0, int> :: x;
โมฆะหลัก (โมฆะ) {}

โพสต์ที่แม่แบบ C ++ เสร็จสมบูรณ์


สำหรับคนที่อยากรู้อยากเห็นคำตอบของ x คือ pow (5,17-depth);
บิน

ซึ่งเป็นที่เรียบง่ายมากที่จะเห็นเมื่อคุณตระหนักว่าข้อโต้แย้งแม่แบบ A และ B K17<Depth+1>::x * 5ทำอะไรและลบออกแล้วแทนที่ทั้งหมดนอกจากนี้ด้วย
David Stone
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.