Các mẫu C ++ Turing-hoàn chỉnh?


111

Tôi được thông báo rằng hệ thống mẫu trong C ++ là hoàn chỉnh Turing tại thời điểm biên dịch. Điều này được đề cập trong bài đăng này và cả trên wikipedia .

Bạn có thể cung cấp một ví dụ quan trọng về phép tính khai thác thuộc tính này không?

Thực tế này có hữu ích trong thực tế không?

Câu trả lời:


110

Thí dụ

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

Đó là một niềm vui nhỏ nhưng không thực tế cho lắm.

Để trả lời phần thứ hai của câu hỏi:
Thực tế này có hữu ích trong thực tế không?

Câu trả lời ngắn gọn: Sắp xếp.

Câu trả lời dài: Có, nhưng chỉ khi bạn là daemon mẫu.

Để tạo ra một chương trình tốt bằng cách sử dụng siêu lập trình mẫu thực sự hữu ích cho người khác sử dụng (tức là một thư viện) thực sự rất khó khăn (mặc dù có thể làm được). Để giúp thúc đẩy thậm chí có MPL aka (Thư viện lập trình Meta). Nhưng hãy thử gỡ lỗi trình biên dịch trong mã mẫu của bạn và bạn sẽ phải trải qua một chặng đường dài khó khăn.

Nhưng một ví dụ thực tế tốt về việc nó được sử dụng cho một thứ hữu ích:

Scott Meyers đã làm việc với các phần mở rộng cho ngôn ngữ C ++ (tôi sử dụng thuật ngữ này một cách lỏng lẻo) bằng cách sử dụng các cơ sở tạo mẫu. Bạn có thể đọc về công việc của anh ấy tại đây ' Thực thi các tính năng mã '


36
Đặng có các khái niệm (poof)
Martin York

5
Tôi chỉ có một vấn đề nhỏ với ví dụ đã cung cấp - nó không khai thác tính hoàn chỉnh (đầy đủ) của Turing-complete hệ thống mẫu của C ++. Thừa có thể được tìm thấy cũng sử dụng các hàm đệ quy nguyên thủy, được không Turing hoàn tất
Dalibor Frivaldsky

4
aand bây giờ chúng ta có khái niệm Lite
Nurettin

1
Trong năm 2017, chúng tôi đang thúc đẩy các khái niệm trở lại xa hơn. Đây là hy vọng cho năm 2020
DeiDei

2
@MarkKegel 12 năm sau: D
Victor

181

Tôi đã thực hiện một máy điều chỉnh trong C ++ 11. Các tính năng mà C ++ 11 thêm vào không thực sự quan trọng đối với máy turing. Nó chỉ cung cấp danh sách quy tắc độ dài tùy ý bằng cách sử dụng các mẫu khác nhau, thay vì sử dụng lập trình siêu mô hình sai lệch :). Tên của các điều kiện được sử dụng để xuất một biểu đồ trên stdout. tôi đã xóa mã đó để giữ cho mẫu ngắn gọn.

#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
Bạn có quá nhiều thời gian trong tay.
Mark Kegel 13/12/08

2
Nó trông giống như ngọng ngoại trừ một từ certin thay thế tất cả các dấu ngoặc đơn đó.
Simon Kuang

1
Nguồn đầy đủ có được công bố công khai ở đâu đó cho người đọc tò mò không? :)
OJFord

1
Chỉ là nỗ lực xứng đáng nhận được nhiều tín dụng hơn :-) Mã này biên dịch (gcc-4.9) nhưng không cung cấp đầu ra - thêm một chút thông tin, như một bài đăng trên blog, sẽ rất tuyệt.
Alfred Bratterud

2
@OllieFord Tôi đã tìm thấy một phiên bản của nó trên trang pastebin và đăng lại nó tại đây: coliru.stacked-crooked.com/a/de06f2f63f905b7e .
Johannes Schaub - litb


13

C ++ của tôi là một chút gỉ, vì vậy có thể không hoàn hảo, nhưng nó gần gũi.

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.

Vấn đề là để chứng minh rằng trình biên dịch đang hoàn toàn đánh giá định nghĩa đệ quy cho đến khi nó đạt được câu trả lời.


1
Umm ... bạn không cần phải có "mẫu <>" ở dòng trước cấu trúc Giai thừa <0> để biểu thị chuyên môn hóa mẫu sao?
paxos1977

11

Để đưa ra một ví dụ không tầm thường: http://gitorious.org/metatrace , trình theo dõi tia thời gian biên dịch C ++.

Lưu ý rằng C ++ 0x sẽ thêm một cơ sở không phải khuôn mẫu, thời gian biên dịch, điều chỉnh hoàn chỉnh ở dạng constexpr:

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

Bạn có thể sử dụng constexpr-expression ở mọi nơi bạn cần biên dịch hằng số thời gian, nhưng bạn cũng có thể gọi constexpr-functions với các tham số không phải const.

Một điều thú vị là điều này cuối cùng sẽ cho phép biên dịch toán học dấu phẩy động thời gian, mặc dù tiêu chuẩn tuyên bố rõ ràng rằng số học dấu chấm động biên dịch thời gian không phải khớp với số học dấu phẩy động thời gian chạy:

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

Không xác định được giá trị của f () là đúng hay sai.



8

Ví dụ về giai thừa thực sự không cho thấy rằng các mẫu là Turing hoàn chỉnh, cũng như nó cho thấy rằng chúng hỗ trợ Đệ quy nguyên thủy. Cách dễ nhất để chứng minh rằng các mẫu là hoàn chỉnh là theo luận điểm của Church-Turing, đó là bằng cách triển khai máy Turing (lộn xộn và hơi vô nghĩa) hoặc ba quy tắc (ứng dụng, abs var) của phép tính lambda không định kiểu. Sau này đơn giản hơn nhiều và thú vị hơn nhiều.

Những gì đang được thảo luận là một tính năng cực kỳ hữu ích khi bạn hiểu rằng các mẫu C ++ cho phép lập trình hàm thuần túy tại thời điểm biên dịch, một chủ nghĩa hình thức thể hiện, mạnh mẽ và thanh lịch nhưng cũng rất phức tạp để viết nếu bạn có ít kinh nghiệm. Cũng lưu ý rằng có bao nhiêu người nhận thấy rằng việc chỉ tạo mã được tạo khuôn mẫu nhiều thường có thể đòi hỏi một nỗ lực lớn: đây chính xác là trường hợp của các ngôn ngữ chức năng (thuần túy), khiến việc biên dịch khó hơn nhưng mang lại hiệu quả đáng ngạc nhiên cho mã không yêu cầu gỡ lỗi.


Này, tôi tự hỏi bạn tham khảo ba quy tắc nào bằng "app, abs, var"? Tôi cho rằng hai đầu tiên là ứng dụng hàm và trừu tượng (định nghĩa lambda (?)) Tương ứng. Là vậy sao? Và cái thứ ba là gì? Một cái gì đó phải làm với các biến?
Wizek

Cá nhân tôi nghĩ rằng nói chung sẽ tốt hơn nếu một ngôn ngữ hỗ trợ Primitive Recursion trong trình biên dịch hơn là để nó là Turing Complete, vì trình biên dịch cho một ngôn ngữ hỗ trợ Primitive Recursion trong thời gian biên dịch có thể đảm bảo rằng bất kỳ bản dựng nào sẽ hoàn thành hoặc không thành công, nhưng không thể có quy trình xây dựng là Turing Complete, ngoại trừ việc hạn chế bản dựng một cách giả tạo để nó không phải là Turing Complete.
supercat

5

Tôi nghĩ nó được gọi là lập trình siêu mẫu .


2
Đây là mặt hữu ích của nó. Nhược điểm là tôi nghi ngờ hầu hết mọi người (và chắc chắn không phải tôi) sẽ thực sự hiểu dù chỉ một phần nhỏ những gì đang xảy ra trong hầu hết những thứ đó. Đó là thứ khủng khiếp không thể đọc được, không thể hiểu được.
Michael Burr

3
Tôi nghĩ đó là nhược điểm của toàn bộ ngôn ngữ C ++. Nó trở thành một con quái vật ...
Federico A. Ramponi

C ++ 0x hứa hẹn sẽ làm cho nó dễ dàng hơn rất nhiều (và theo kinh nghiệm của tôi, vấn đề lớn nhất là các trình biên dịch không hỗ trợ nó đầy đủ, điều mà C ++ 0x sẽ không giúp được gì). Cụ thể, các khái niệm có vẻ như chúng sẽ làm sáng tỏ mọi thứ, như loại bỏ rất nhiều nội dung SFINAE, khó đọc.
coppro 9/10/08

@MichaelBurr Ủy ban C ++ không quan tâm đến những thứ không thể đọc được, không thể xác minh được; họ chỉ thích thêm các tính năng.
Sapphire_Brick

4

Chà, đây là thời gian biên dịch triển khai Máy Turing chạy một hải ly bận 4 trạng thái 2 ký hiệu

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

Chạy thử nghiệm Ideone: https://ideone.com/MvBU3Z

Giải trình: http://victorkomarov.blogspot.ru/2016/03/compile-time-turing-machine.html

Github với nhiều ví dụ hơn: https://github.com/fnz/CTTM


3

Bạn có thể kiểm tra bài viết này từ Tiến sĩ Dobbs về triển khai FFT với các mẫu mà tôi nghĩ không phải là tầm thường. Điểm chính là cho phép trình biên dịch thực hiện tối ưu hóa tốt hơn so với việc triển khai không theo khuôn mẫu vì thuật toán FFT sử dụng rất nhiều hằng số (ví dụ: bảng sin)

phần tôi

Phần II


2

Thật thú vị khi chỉ ra rằng nó là một ngôn ngữ chức năng thuần túy mặc dù gần như không thể gỡ lỗi. Nếu bạn nhìn vào bài đăng của James, bạn sẽ thấy ý tôi là nó hoạt động. Nói chung, nó không phải là tính năng hữu ích nhất của C ++. Nó không được thiết kế để làm điều này. Đó là thứ đã được khám phá.


2

Nó có thể hữu ích nếu bạn muốn tính hằng số tại thời điểm biên dịch, ít nhất là trên lý thuyết. Kiểm tra lập trình siêu mẫu .


1

Một ví dụ hữu ích hợp lý là một lớp tỷ lệ. Có một vài biến thể nổi xung quanh. Bắt trường hợp D == 0 khá đơn giản với quá tải một phần. Máy tính thực sự là tính toán GCD của N và D và thời gian biên dịch. Điều này là cần thiết khi bạn đang sử dụng các tỷ lệ này trong tính toán thời gian biên dịch.

Ví dụ: Khi bạn đang tính cm (5) * ki lô mét (5), tại thời điểm biên dịch, bạn sẽ nhân tỷ lệ <1.100> và tỷ lệ <1000,1>. Để ngăn tràn, bạn muốn tỷ lệ <10,1> thay vì tỷ lệ <1000,100>.


0

Một máy Turing là hoàn chỉnh, nhưng điều đó không có nghĩa là bạn nên sử dụng một máy cho mã sản xuất.

Theo kinh nghiệm của tôi, cố gắng làm bất cứ điều gì không tầm thường với các khuôn mẫu là lộn xộn, xấu xí và vô nghĩa. Bạn không có cách nào để "gỡ lỗi" "mã" của mình, thông báo lỗi thời gian biên dịch sẽ khó hiểu và thường ở những nơi khó xảy ra nhất và bạn có thể đạt được cùng một lợi ích về hiệu suất theo những cách khác nhau. (Gợi ý: 4! = 24). Tệ hơn nữa, mã của bạn không thể hiểu được đối với các lập trình viên C ++ trung bình và có thể sẽ không di động do có nhiều mức hỗ trợ trong các trình biên dịch hiện tại.

Các mẫu rất tốt cho việc tạo mã chung (lớp chứa, trình bao bọc lớp, kết hợp), nhưng không - theo ý kiến ​​của tôi, Tính đầy đủ Turing của các mẫu KHÔNG HỮU ÍCH trong thực tế.


4! có thể là 24, nhưng MY_FAVORITE_MACRO_VALUE là gì! ? OK, tôi thực sự cũng không nghĩ đây là một ý kiến ​​hay.
Jeffrey L Whitledge

0

Một ví dụ khác về cách không lập trình:

mẫu <int Depth, int A, typename B>
struct K17 {
    tĩnh const int x =
    K17 <Độ sâu + 1, 0, K17 <Độ sâu, A, B>> :: x
    + K17 <Độ sâu + 1, 1, K17 <Độ sâu, A, B>> :: x
    + K17 <Độ sâu + 1, 2, K17 <Độ sâu, A, B>> :: x
    + K17 <Độ sâu + 1, 3, K17 <Độ sâu, A, B>> :: x
    + K17 <Độ sâu + 1, 4, K17 <Độ sâu, A, B>> :: x;
}
mẫu <int A, typename B>
struct K17 <16, A, B> {static const int x = 1; }
static const int z = K17 <0,0, int> :: x;
void main (void) {}

Đăng tại C ++ các mẫu đã hoàn tất


đối với những người tò mò, câu trả lời cho x là pow (độ sâu 5,17);
flownt

Điều này đơn giản hơn nhiều để xem khi bạn nhận ra rằng các đối số mẫu A và B không làm gì cả và xóa chúng, sau đó thay thế tất cả phép cộng bằng K17<Depth+1>::x * 5.
David Stone,
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.