Портирование C ++ на C # - шаблоны - PullRequest
5 голосов
/ 19 января 2011

Я портирую приложение C ++ на C # и использую шаблоны. Я немного прочитал об этом, и я понимаю, что некоторые шаблоны сродни .Net дженерикам. Я прочитал SO ответ на этот случай, который хорошо подвел итог.

Тем не менее, некоторые виды использования шаблонов c ++, похоже, не связаны напрямую с дженериками. В приведенном ниже примере из статьи Шаблонное метапрограммирование Википедии шаблон, кажется, принимает значение, а не тип. Я не совсем уверен, как это будет перенесено на C #?

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

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

// Factorial<4>::value == 24
// Factorial<0>::value == 1
void foo()
{
    int x = Factorial<4>::value; // == 24
    int y = Factorial<0>::value; // == 1
}

Ясно, что для этого примера я мог бы сделать:

public int Factorial(int N){
    if(N == 0) return 1;
    return Factorial(N - 1);
}

но мне кажется, что это рефакторинг функции, а не порт семантически подобного кода.

Ответы [ 5 ]

5 голосов
/ 19 января 2011

В приведенном ниже примере ... кажется, что шаблон принимает значение, а не тип.

Это не самая большая проблема.Фактически, это может быть теоретически решено в C # с использованием церковной цифры или представления Пеано, основанного на вложенных универсальных типах. 1

Однако Ваша проблема в том, что C # не допускает специализацию шаблона .В вашем примере специализация шаблона отвечает за определение того, что факториал 0 равен 1, а не такой же, как для всех других чисел.C # не позволяет делать это.

Таким образом, нет способа указать базовый случай в определении рекурсивного шаблона (универсального) и, следовательно, нет рекурсии.Обобщения C # не являются полными по Тьюрингу, а шаблоны C ++:


1 Примерно так:

class Zero { }

class Successor<T> : Zero where T : Zero { }

// one:
Successor<Zero>
// two:
Successor<Successor<Zero>>
// etc.

Выполнение операций с этими числами оставлено какупражнение для читателя.

5 голосов
/ 19 января 2011

К сожалению, дженерики .Net могут принимать только типы.Шаблоны C ++ принимают другие значения, которые компилятор считает константными выражениями, потому что они фактически являются просто макросами, которые расширяются до большего количества кода.

Это означает, что ваша идея превратить код в вызов метода является наилучшей.Вы можете сделать так, чтобы вызов метода возвращал тип со свойством .Value (следуя вашему примеру), таким образом сохраняя перенесенный код похожим на шаблон:

return Factorial(N-1).Value;
3 голосов
/ 19 января 2011

Посмотрите в этой статье различия между шаблонами C # и шаблонами c ++:

Я думаю, что ваш пример включен там.

MSDN Link

1 голос
/ 19 января 2011

Короткий ответ заключается в том, что не все, что можно сделать в шаблонах C ++, можно сделать в обобщениях C #.В случае шаблонов, которые принимают значения, не относящиеся к типу, каждая ситуация должна обрабатываться и пересчитываться соответствующим образом в каждом конкретном случае.

0 голосов
/ 19 января 2011

Это так близко, как я мог подумать:

public class Factorial<T>
    where T : IConvertible 
    {
        public T GetFactorial(T t)
        {
            int int32 = Convert.ToInt32(t);
            if (int32 == 0)
                return (T) Convert.ChangeType( 1, typeof(T));
            return GetFactorial( (T) Convert.ChangeType(int32-1, typeof(T)) );
        }
    }

Проблема в том, что вы не можете определить дженерики и ограничить его ValueTypes. Это будет работать для байтов, Int16 и Int32 . Также для небольших значений Int64 .

...