дженерики JAVA в с ++?как сделать? - PullRequest
1 голос
/ 11 августа 2011
class T : public std::string {  
public:  
    T(char* s) : std::string(s){};  
};  

class X : public T {  
public:  
    X(char* s) : T(s) {};  
    ~X() {};  
};  

template <typename T> T doIt(const T arg);  

int main(int argc, const char* argv[]) {  

    X s("initial string");  
    T s2 = doIt(s);  
    printf("out %s", s2.c_str());  
}  

T doIt(T arg) {  
    arg.append(" appended");  
    return arg;  
};

В чем проблема с моим кодом ... вывод ниже ...

1> Связывание ...
1> TemplateStuding1.obj: ошибка LNK2001: неразрешенный внешний символ "класс X __cdecl doIt (класс X) "(?? $ doIt @ VXClass @@@@ YA? AVXClass @@ V0 @@ Z) * ​​1005 * 1> D: \ Programming \ cpp \ cpp-how-to-program \CppHowToProgram \ Release \ Test.exe: фатальная ошибка LNK1120: 1 неразрешенный внешний код

Ответы [ 3 ]

4 голосов
/ 11 августа 2011

Как только вы преодолеете проблему с отсутствующим template <class T> в вашем определении doIt (которое уже упоминалось другими), вы все равно захотите ответить на свой вопрос:

Можно ли сделать аргумент шаблона <X extends T> как в Java?

Ответ на этот вопрос - нет. C ++ не имеет обобщенных обобщений, как в Java. Было внесено предложение добавить что-то подобное (но сильно отличающееся) под названием «концепции» в C ++ 0x, но это было слишком сложно и было отброшено.

Короче говоря, есть 3 способа сделать дженерики, которые имеют отношение:

  1. Утиная печать (что есть у Руби). Это то, что C ++ имеет сейчас. Если класс отвечает на все те же методы, что и class T, то он будет соответствовать требованиям, даже если он не унаследован от класса T. Кроме того, если вы попытаетесь передать class Z, в котором отсутствуют некоторые методы, class T имеет, вы никогда не узнаете, если шаблон не пытается вызвать их. Если шаблон попытается вызвать их, то в том месте, где шаблон пытается вызвать отсутствующий метод, появится ошибка компилятора. (Вы узнаете, какой экземпляр шаблона вызвал проблему, по трассировке стека, которую компилятор выдаст, объясняя, какие шаблоны он пытался создать при возникновении ошибки.) Вы do получаете ошибку компилятора в C ++ (в отличие от Ruby, где это ошибка времени выполнения), но это сложное сообщение об ошибке.

  2. Структурная типизация (что есть в Scala). Концепции в C ++ были предназначены для продвижения в этом направлении. Если класс отвечает на все те же методы, что и class T, тогда он будет соответствовать требованиям, даже если он не унаследован от класса T. Если этот класс не отвечает на все те же методы, это ошибка, даже если функция шаблона не пытается вызвать отсутствующий метод. Об ошибках шаблона сообщается на сайте реализации. (Версия C ++ этого будет более сложной, потому что вы можете объявить операторы на объекте как свободные функции, но основная идея та же самая.)

  3. Ограниченные дженерики (из-за отсутствия лучшего термина - что есть в Java). Любой класс, переданный в шаблон , должен быть подтипом class T. Наличие тех же самых методов не сократит это, если нет реального наследования. Членам комитета по стандартизации C ++ это не нравится - они предпочитают № 2, а не № 1 (если они могут решить все технические проблемы) и № 1, а не № 3 - так что, вероятно, он никогда не появится в C ++.


Публикует ли кто-нибудь еще ответ, разобравший использование этого парня class T в этом примере. Он использует его двумя различными способами, и я не уверен, что его использование T в template <class T> (в отличие от какой-либо другой буквы) предназначалось для указания ограничения на типы, которые могут быть переданы. Это может быть существенной путаницей. С другой стороны, использование T в обоих местах может быть просто небрежной ошибкой. Я действительно не могу сказать.

1 голос
/ 11 августа 2011

Вот проблема:

template <typename T> T doIt(const T arg);  // <---- declared but not defined

int main(int argc, const char* argv[]) {  
//...
    T s2 = doIt(s);  // <------ it is calling above method (which is visible)
}  

T doIt(T arg) {  // <------ T is `class T`, not `template`, so different method
    arg.append(" appended");  
    return arg;  
};

Здесь, когда вы определяете T doIt(T) после main(), вы ожидаете, что определяете тело вышеупомянутого template метода. Что не соответствует действительности. Вы не получите ошибку компилятора, потому что, по совпадению, у вас есть class T; который пройдет определение T doIt(T).

Если вы намереваетесь использовать template doIt, тогда ваше определение должно быть таким,

template<typename T>
T doIt(T arg) {  // <------ T is now `template`; thus same method
    arg.append(" appended");  
    return arg;  
};

[Также обратите внимание, что вы получаете ошибку компоновщика, потому что у вас нет реального определения template doIt, и какое бы определение у вас не было ниже main(), оно не было видно.]

0 голосов
/ 11 августа 2011

Ваш компилятор жалуется, что вы объявили, но никогда не реализовали doIt. Все, что сейчас есть, это подпись, и все же вы называете это так, как будто она определена.

Кстати, какое отношение эта ошибка имеет к Java? Или даже дженерики?

...