Почему я не могу создать шаблонный подкласс System :: Collections :: Generic :: IEnumerable <T>? - PullRequest
4 голосов
/ 02 апреля 2010

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

Вот простая версия того, что я придумал, которая демонстрирует мою проблему:

ref class A {};

template<class B>
ref class Test : public System::Collections::Generic::IEnumerable<B^> // error C3225...
{};

void test()
{
    Test<A> ^a = gcnew Test<A>();
}

В указанной строке я получаю эту ошибку:

ошибка C3225: аргумент универсального типа для 'T' не может быть 'B ^', это должен быть тип значения или дескриптор ссылочного типа

Если я использую другой родительский класс, я не вижу проблемы:

template<class P>
ref class Parent {};

ref class A {};

template<class B>
ref class Test : public Parent<B^> // no problem here
{};

void test()
{
    Test<A> ^a = gcnew Test<A>();
}

Я могу обойти это, добавив другой параметр шаблона к типу реализации:

ref class A {};

template<class B, class Enumerable>
ref class Test : public Enumerable
{};

void test()
{
    using namespace System::Collections::Generic;
    Test<A, IEnumerable<A^>> ^a = gcnew Test<A, IEnumerable<A^>>();
}

Но мне это кажется грязным. Кроме того, я просто хотел бы понять, что здесь происходит - почему не работает первый способ?

1 Ответ

3 голосов
/ 03 апреля 2010

В первом примере ваша строка наследования должна выглядеть следующим образом:

ref class Test : public System::Collections::Generic::IEnumerable<B>

(на шаблоне нет ссылочного маркера)

Тогда ваша строка использования должна выглядеть так:

Test<A^> ^a = gcnew Test<A^>();

Маркеры ссылок используются в экземпляре шаблона, а не в самом шаблоне.

Вот ваш пример, скомпилированный:

using namespace System;
using namespace System::Collections::Generic;

ref class A {};

template<class B> ref class Test : public System::Collections::Generic::IEnumerable<B>
{
public:
    B GetInstance()
    {
        return Activator::CreateInstance<B>();
    }

    virtual System::Collections::IEnumerator^ GetEnumeratorObj() =
        System::Collections::IEnumerable::GetEnumerator
    {
        return nullptr;
    }

    virtual System::Collections::Generic::IEnumerator<B>^ GetEnumerator()
    {
        return nullptr;
    }
};

void test()
{
    Test<A^> ^a = gcnew Test<A^>();
}

Редактировать: Понял, я должен объяснить, почему это так. Насколько я понимаю, причина, по которой вы не можете указать B ^ в наследовании IEnumerable, заключается в том, что IEnumerable является универсальным с ограничением на него, а B - это параметр шаблона, который не ограничен. Шаблоны допускают гораздо более гибкий синтаксис, даже когда они управляют объектами ref, поскольку они по-прежнему эффективно «разбирают текст» даже в C ++ / CLI. Однако, когда они сталкиваются с дженериками с ограничениями, правила становятся намного жестче.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...