没有依赖于模板参数的参数

82
我将尝试完成以下内容:
template <class T>
std::ifstream& operator>> (std::ifstream& fin, List<T> l)
{
    T temp;
    l.resize(0);
    fin >> ignore(1,'\t');
    for(ListIterator<T> i=l.begin();i!=l.end();i++)
    {
        fin >> ignore(1,'\t') >> temp;
        l.push_back(temp);
    }
    return fin;
}

我需要从一个文件中读取所有的内容。每个字段都是由'\t'字符分隔开的,因此我需要忽略'\t'字符。

以下是错误日志:

/home/ramy/Documents/C++/Prova/Util.h||In function ‘std::ifstream& Util::operator>> (std::ifstream&, Util::List<T>)’:|
/home/ramy/Documents/C++/Prova/Util.h|431|error: there are no arguments to ‘ignore’ that  depend on a template parameter, so a declaration of ‘ignore’ must be available|
/home/ramy/Documents/C++/Prova/Util.h|431|note: (if you use ‘-fpermissive’, G++ will  accept your code, but allowing the use of an undeclared name is deprecated)|
||=== Build finished: 1 errors, 0 warnings ===|

6
最好复制粘贴错误信息以进行搜索,即您的标题中有一个拼写错误:“tamplate”。 - Sebastian Mach
6个回答

66

12
链接现在已经失效了。:( - fiktor
2
@sidewinderguy - 我也遇到了完全相同的问题。我试图仔细阅读上面phresnel的答案以获取一些关于它的知识,但是“为什么”仍然让我困惑。太糟糕了,链接已经失效了。:( - Syndog
7
“这就是为什么C++应该放弃并让位给Java。”这句话让我感到震惊,我是否是唯一一个有这种感觉的人? - Benoit Blanchon
1
针对哪个this?问题中的代码和错误信息没有涉及成员函数。 - Sebastian Mach
6
@Syndog:实际上,“this”解决方案并没有帮助到OP的情况,因此John的答案是不正确的。但是,在模板类成员函数的情况下(即模板类的成员函数(例如Foo<T>::ignore()),“this->ignore”强制编译器将“ignore”解释为_依赖名称_,只有在第二阶段完全查找才能实现。这背后的动机在我的回答中解释了 :) - Sebastian Mach
2
@BenoitBlanchon:不,你不是。虽然我承认C++的语法很丑陋,但人们不能指望利用非常复杂的语言特性(“嘿,让我们编写一个模板函数,成为酷酷的孩子”)而不知道非常基本的错误消息和机制。不知道没有错(我们都曾一无所知,对吧?),但是因为对高级功能的基本非知识而推荐杀死C++,这种行为最多是无能。 “嘿,那些长名称和依赖反转痉挛是什么?这就是为什么Java应该...” - Sebastian Mach

64
对于内置类型,不执行参数相关查找(ADL),因此必须将ignore符号“导入”到当前命名空间中。
例如,您可以按照以下方式做:从最受欢迎到最不受欢迎(即最具侵入性和名称污染):
  • foobar::ignore (...)
  • using foobar::ignore; ignore(...);
  • using namespace foobar; ignore(...);
错误消息会出现这样的原因是在模板中,您还进入了依赖名称和两阶段查找的领域。依赖于模板参数的名称,例如
template <typename T> void foo() {
    T x;
    x.frobnicate();
}

第二阶段进行查找,即在实例化时。不依赖于模板参数的名称,如

class Foo {};

template <typename T> void foo() {
    Foo foo;
    foo.frobnicate();
}

必须在第一阶段中可解析。

此分离有助于模板作者更早地发现错误并找到正确的符号,并且有助于使模板更通用。例如,在C#泛型中,必须解决所有问题,这对其灵活性施加了相当严格的限制(因为泛型可能使用的所有内容都必须定义)。相反,一些旧的C++编译器仅在第二阶段解析,即在实例化时进行,这对查找和错误查找产生了一些微妙的影响。

C++的两阶段模型结合了急切模型(C#)和懒惰模型(一些旧的C++编译器)的优点。


16

这个错误消息意味着编译器在这一点上无法使用ignore的定义。如果您执行以下操作,会得到完全相同的错误:

void f() {
   g();
}
void g() {}

请注意,即使代码看起来非常不同,也可以使用这种方式。需要指出的是,与其他回答所说的ADL问题无关。错误消息变得如此复杂的原因在于编译器处理模板的方式。

模板分为两个阶段进行处理,在第一个阶段中,必须验证所有不依赖于实例化类型的内容,而且必须在不执行类型替换的情况下进行。在此阶段中,必须检查每个非相关名称,在本例中,编译器未能使用模板定义处的声明解析ignore

如果表达式取决于模板的类型参数,则在第一次处理期间不需要完全解析它,并且会在实例化处可用的声明后再次尝试它。


2
这对我有用,哈哈,9年后。 - HBatalha

6
我曾遇到类似问题,通过更改包含的顺序解决了问题。
正如 phresnel 所说,编译器在第一阶段无法解析此问题,因为包含有问题的模板方法的头文件在包含内部无法解析的方法的头文件之前。
添加所需的头文件包含后,我成功解决了这个错误。希望能对其他人有帮助。

2
我不知道你的天气问题是否已经解决,希望已经解决了。
每当我遇到“没有依赖于模板参数的参数”问题时,我会重写该方法并调用父类成员函数。
为了说明我的意思,请考虑下面这个模板类ADT。
template <typename DataTypeOfNode>
class LinearList
{
public:
    LinearList(){}
    void addAtBeg(DataTypeOfNode data) {
        //Inside implementation....
    }
    DataTypeOfNode removeFromFront() {
        //Inside implementation....
    } 

    // And many more useful methods
    ~LinearList(){}

};

现在,如果您使用如下代码将此类继承到一个基类中,比如"PriorityQueue"
template <typename DataTypeOfNode>
class PriorityQueue : public LinearList<DataTypeOfNode>
{
public:

    void enqueue(DataTypeOfNode data){
        addAtBeg(data);
    }

    DataTypeOfNode dequeue(){
        return removeFromFront() ; 
    }
    PriorityQueue(){}
    ~PriorityQueue(){}
};

编译后你将会收到如下错误信息:"There are no arguments that depend on a template parameter",该错误信息是针对 removeFromFront()addAtBeg() 方法的模板参数。

为了解决该错误,你需要重写这两个方法并调用父类的方法,代码如下:

template <typename DataTypeOfNode>
class PriorityQueue : public LinearList<DataTypeOfNode>
{
public:

    //Calling parent class methods

    void addAtBeg(DataTypeOfNode data){
        LinearList<DataTypeOfNode>::addAtBeg(data) ; 
    }

    DataTypeOfNode removeFromFront(){
        return LinearList<DataTypeOfNode>::removeFromFront() ; 
    }

    void enqueue(DataTypeOfNode data){
        addAtBeg(data);
    }

    DataTypeOfNode dequeue(){
        return removeFromFront() ; 
    }
    PriorityQueue(){}
    ~PriorityQueue(){}
};

1
这意味着编译器找不到 ignore,因此 ADL 无法启动。这意味着没有适当作用域的 ignore 函数。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接