C ++ 템플릿 Turing-complete?


답변:


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 (메타 프로그래밍 라이브러리)도 있습니다. 그러나 템플릿 코드에서 컴파일러 오류를 디버깅하면 오래 걸릴 것입니다.

그러나 유용한 것을 위해 사용되는 좋은 실용적인 예 :

Scott Meyers는 템플릿 기능을 사용하여 C ++ 언어 (저는 용어를 느슨하게 사용함)에 대한 확장을 작업했습니다. 그의 작업에 대한 내용은 ' 강제 코드 기능 '에서 확인할 수 있습니다.


36
Dang 거기에 개념 (휙)
Martin York

5
제공된 예제에는 작은 문제가 있습니다. C ++ 템플릿 시스템의 (전체) 튜링 완전성을 악용하지 않습니다. 팩토리얼은 튜링 완료가 아닌 원시 재귀 함수를 사용하여도 찾을 수 있습니다
Dalibor Frivaldsky

4
그리고 이제 우리는 개념을
가졌습니다

1
2017 년에 우리는 개념을 훨씬 더 뒤로 밀고 있습니다. 여기에 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
모든 괄호를 대체하는 certin 단어를 제외하고는 lisp처럼 보입니다.
Simon Kuang

1
호기심 많은 독자를 위해 어딘가에서 전체 소스를 공개적으로 사용할 수 있습니까? :)
OJFord

1
시도는 더 많은 신용을받을 가치가 있습니다. :-)이 코드는 컴파일되지만 (gcc-4.9) 출력은 제공하지 않습니다. 블로그 게시물과 같은 정보가 조금 더 있으면 좋을 것입니다.
Alfred Bratterud

2
@OllieFord 저는 pastebin 페이지에서 버전을 찾아 여기에 다시 붙여 넣었습니다 : coliru.stacked-crooked.com/a/de06f2f63f905b7e .
Johannes Schaub-litb


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
음 ... 템플릿 전문화를 나타 내기 위해 struct Factorial <0> 앞 줄에 "template <>"이 필요하지 않습니까?
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가 아닌 매개 변수를 사용하여 -functions를 호출 할 수도 있습니다 .

한 가지 멋진 점은 컴파일 시간 부동 소수점 산술이 런타임 부동 소수점 산술과 일치 할 필요가 없다고 표준에 명시 적으로 명시되어 있지만 이것이 마침내 컴파일 시간 부동 소수점 연산을 활성화한다는 것입니다.

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

팩토리얼 예제는 실제로 템플릿이 Primitive Recursion을 지원한다는 것을 보여주는만큼 튜링이 완전하다는 것을 보여주지 않습니다. 템플릿이 완전하다는 것을 보여주는 가장 쉬운 방법은 Church-Turing 논문을 사용하는 것입니다. 즉, Turing 머신 (지저분하고 약간 무의미 함) 또는 형식화되지 않은 람다 미적분의 세 가지 규칙 (app, abs var)을 구현하는 것입니다. 후자는 훨씬 간단하고 훨씬 흥미 롭습니다.

논의중인 것은 C ++ 템플릿이 컴파일 타임에 순수 함수형 프로그래밍을 허용한다는 사실을 이해할 때 매우 유용한 기능입니다. 이는 표현력이 풍부하고 강력하며 우아하지만 경험이 거의없는 경우 작성하기 매우 복잡한 형식주의입니다. 또한 얼마나 많은 사람들이 템플릿 화가 심한 코드를 얻는 데 종종 큰 노력이 필요할 수 있다는 사실을 알아 차리십시오. 이것은 정확히 (순수한) 함수 언어의 경우입니다. 이는 컴파일을 더 어렵게하지만 놀랍게도 디버깅이 필요하지 않은 코드를 생성합니다.


"app, abs, var"에서 언급 한 세 가지 규칙은 무엇입니까? 처음 두 가지는 각각 함수 응용과 추상화 (람다 정의 (?))라고 가정합니다. 그렇습니까? 그리고 세 번째는 무엇입니까? 변수와 관련이 있습니까?
Wizek

나는 개인적으로 컴파일 타임 프리미티브 재귀를 지원하는 언어의 컴파일러가 모든 빌드가 완료되거나 실패 할 것을 보장 할 수 있기 때문에 일반적으로 컴파일러에서 프리미티브 재귀를 지원하는 것이 튜링 완료보다 낫다고 생각합니다. 빌드 프로세스가 Turing Complete 인 사람은 빌드를 인위적으로 제한하여 Turing Complete가 아닌 경우를 제외하고는 할 수 없습니다.
supercat

5

템플릿 메타 프로그래밍 이라고 생각합니다 .


2
이것이 유용한 측면입니다. 단점은 대부분의 사람들 (저는 아님)이 대부분의 일에서 일어나는 일의 작은 비율조차도 실제로 이해하지 못할 것이라는 점입니다. 끔찍하게 읽을 수없고 유지 관리 할 수없는 것입니다.
Michael Burr

3
이것이 전체 C ++ 언어의 단점이라고 생각합니다. 괴물이되고 있습니다 ...
Federico A. Ramponi

C ++ 0x는 많은 것을 더 쉽게 만들 것이라고 약속합니다 (내 경험상 가장 큰 문제는 C ++ 0x가 도움이되지 않는 컴파일러를 완전히 지원하지 않는다는 것입니다). 특히 개념은 읽기 어려운 SFINAE 항목을 많이 제거하는 것과 같이 일을 정리하는 것처럼 보입니다.
coppro

@MichaelBurr C ++위원회는 읽을 수없고 관리 할 수없는 것에 대해서는 신경 쓰지 않습니다. 그들은 기능을 추가하는 것을 좋아 합니다.
Sapphire_Brick

4

음, 여기에 4- 상태 2- 기호 바쁜 비버를 실행하는 컴파일 타임 Turing Machine 구현이 있습니다.

#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

FFT 구현에 대한 Dr. Dobbs의이 기사를 내가 그렇게 사소하지 않다고 생각하는 템플릿으로 확인할 수 있습니다. 요점은 FFT 알고리즘이 많은 상수 (예 : sin 테이블)를 사용하므로 컴파일러가 템플릿이 아닌 구현보다 더 나은 최적화를 수행 할 수 있도록하는 것입니다.

파트 I

파트 II


2

디버그가 거의 불가능하지만 순전히 기능적인 언어라는 점을 지적하는 것도 재미 있습니다. James post 를 보면 그것이 기능적이라는 것을 알 수 있습니다. 일반적으로 C ++의 가장 유용한 기능은 아닙니다. 이를 수행하도록 설계되지 않았습니다. 발견 된 것입니다.



1

합리적으로 유용한 예는 비율 클래스입니다. 주위에 떠 다니는 몇 가지 변종이 있습니다. D == 0 케이스를 잡는 것은 부분적인 과부하로 매우 간단합니다. 실제 계산은 N과 D의 GCD와 컴파일 시간을 계산하는 것입니다. 이것은 컴파일 타임 계산에서 이러한 비율을 사용할 때 필수적입니다.

예 : 센티미터 (5) * km (5)를 계산할 때 컴파일 타임에 ratio <1,100>과 ratio <1000,1>을 곱하게됩니다. 오버플로를 방지하려면 ratio <1000,100> 대신 ratio <10,1>을 원합니다.


0

튜링 기계 튜링 완료,하지만 당신이 생산 코드를 사용하고자한다는 의미하지 않습니다.

템플릿으로 사소하지 않은 것을 시도하는 것은 내 경험에 지저분하고 추하고 무의미합니다. "코드"를 "디버그"할 수있는 방법이 없습니다. 컴파일 시간 오류 메시지는 모호하고 일반적으로 가능성이 가장 낮은 위치에 있으며 다른 방법으로 동일한 성능 이점을 얻을 수 있습니다. (힌트 : 4! = 24). 더 나쁜 것은 여러분의 코드가 일반 C ++ 프로그래머가 이해할 수 없으며 현재 컴파일러 내에서 광범위한 지원 수준으로 인해 이식이 불가능할 가능성이 있다는 것입니다.

템플릿은 일반적인 코드 생성 (컨테이너 클래스, 클래스 래퍼, 믹스 인)에 적합하지만, 아닙니다. 제 생각에는 템플릿의 튜링 완전성은 실제로 유용하지 않습니다 .


4! 24 일 수 있지만 MY_FAVORITE_MACRO_VALUE는 무엇입니까? ? 좋아요, 저는 이것이 좋은 생각이라고 생각하지 않습니다.
Jeffrey L Whitledge

0

프로그래밍하지 않는 방법의 또 다른 예 :

template <int Depth, int A, typename B>
struct 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, typename B>
struct 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.