1
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:无法解析的外部符号“class X __cdecl doIt(class X)”(??$doIt@VXClass@@@@YA?AVXClass@@V0@@Z)
1 >D:\Programming\cpp\cpp-how-to-program\CppHowToProgram\Release\Test.exe : 致命错误 LNK1120: 1 unresolved externals

4

3 回答 3

4

一旦您解决了(其他人已经提到的)template <class T>定义中缺少的问题,您仍然需要回答您的问题:doIt

<X extends T>是否可以像在 Java 中一样制作模板参数?

这个问题的答案是否定的。C++ 不像 Java 那样具有受约束的泛型。桌上有一个提议,要在 C++0x 中添加类似(但有很大不同)的东西,称为“概念”,但它太复杂了,已被删除。

简而言之,有 3 种方法可以做相关的泛型:

  1. 鸭子打字(Ruby 有)。这就是 C++ 现在所拥有的。如果一个类响应所有相同的方法,class T那么它将符合要求,即使它不是从类 T 继承的。此外,如果您尝试传递class Z缺少某些class T具有的方法,您将永远不会发现是否模板不会尝试调用它们。如果模板确实尝试调用它们,那么在模板尝试调用缺失方法的地方会出现编译器错误。(您会从堆栈跟踪中找出哪个模板实例化导致了问题,编译器将吐出解释它在遇到错误时尝试实例化的模板。)您确实会在 C++ 中遇到编译器错误(与 Ruby 不同,其中这是一个运行时错误),但它是一个复杂的错误消息。

  2. 结构类型(Scala 有)。C++ 中的概念旨在朝着这个方向发展。如果一个类响应所有相同的方法,class T那么它将符合要求,即使它不是从类 T 继承的。如果该类没有响应所有相同的方法,即使模板函数也是错误的不会尝试调用缺少的方法。在实例化站点报告模板错误。(C++ 的版本会更复杂,因为您可以将对象上的运算符声明为自由函数,但基本思想是相同的。)

  3. 受约束的泛型(因为没有更好的术语——Java 有)。传递给模板的任何类都必须class T. 除非有真正的继承,否则拥有相同的方法不会削减它。C++ 标准化委员会的成员不喜欢这样——他们更喜欢 #2 而不是 #1(如果他们可以解决所有技术问题)和 #1 而不是 #3——所以它可能永远不会出现在 C++ 中。


请其他人发布一个答案,解开这个人class T在这个例子中的使用。他以两种不同的方式使用它,我不确定他使用Tin template <class T>(而不是其他字母)是否是为了指定对可以传递的类型的约束。这可能是一个严重的混淆。另一方面,T在这两个地方使用可能只是一个粗心的错误。我真的说不出来。

于 2011-08-11T05:45:41.980 回答
1

这是问题所在:

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)after时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()都不可见。]

于 2011-08-11T05:24:13.533 回答
0

您的编译器抱怨您声明但从未实现doIt。它现在只是一个签名,而你却像实际定义的那样称呼它。

顺便说一句,这个错误到底与java有什么关系?甚至是泛型?

于 2011-08-11T04:59:41.460 回答