在C++模板函数中,为什么依赖函数调用给出"未声明"错误?

Sha*_*ugh 5 c++

在C++模板函数foo()中,对:: bar(TT*)的调用在gcc 4.4.3下给出以下错误:

g++ -o hello.o -c -g hello.cpp
hello.cpp: In function 'void foo(std::vector<TT*, std::allocator<TT*> >&)':
hello.cpp:8: error: '::bar' has not been declared
Run Code Online (Sandbox Code Playgroud)

这是有问题的代码:

// hello.cpp

#include <vector>

template<typename TT> void foo(std::vector<TT*> &vec)
{
    TT *tt;
    ::bar(tt);
    vec.push_back(tt);
}

class Blah
{
};

void bar(Blah *&)
{
}

int main(int argc, char *argv[])
{
    std::vector<Blah*> vec;
    foo(vec);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

C++区分依赖于模板参数(此处为TT)的符号和独立且可立即求值的符号.

很明显,编译器认为我的:: bar(TT*)调用是独立的,并尝试立即解决它.正如显然,该函数调用依赖于TT,因为函数调用采用类型TT*的参数,所以编译器应该等到把foo(VEC)实例解析::巴(TT*).

这是一个gcc错误还是我遗漏了一些关于C++模板的微妙之处?

编辑:这是一个稍微复杂的例子,有两个版本的:: bar()来澄清声明顺序不是问题的问题.在解析模板时,编译器无法知道下面的main()是否将使用TT = Blah或TT = Argh来实例化模板函数.因此,编译器不应该在最早(如果有的话)第35行第28行之前给出错误.但是第8行第16 给出了错误.

编辑#2:改进了这个例子.

编辑#3:对此示例添加了更正,以使其按预期工作.bar(tt)现在正确指向bar(Blah*).基本原理如下.(感谢大家).

// hello.cpp
#include <vector>

class XX {};
void bar(XX*) {}

class CC {
public:
    void bar();
    void bar(int *);
    void bar(float *);

    template<typename TT> static void foo(std::vector<TT*> &vec);
};

template<typename TT>
void CC::foo(std::vector<TT*> &vec) {
    using ::bar;
    TT *tt;
    bar(tt);
    vec.push_back(tt);
}

class Argh {};
void bar(Argh *&aa) { aa = new Argh; }

class Blah {};
void bar(Blah *&bb) { bb = new Blah; }

int main(int argc, char *argv[]) {
    std::vector<Blah*> vec;
    CC::foo(vec);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Chr*_*odd 5

从C++规范的第14.7.2节:

在表达式中:

    postfix-expression ( expression-listopt )
Run Code Online (Sandbox Code Playgroud)

其中postfix-expression是unqualified-id但不是template-id,当且仅当表达式列表中的任何表达式是依赖于类型的表达式(14.7.2.2)时,unqualified-id表示依赖名称.

因为::b不是不合格的id,所以它不是依赖名称.如果删除它::,则它是一个不合格的名称,因此是一个从属名称.对于非依赖名称,查找发生在模板声明的点,而不是实例化,并且此时没有bar可见的全局声明,因此您会收到错误.


Joh*_*itb 5

目前还没有人指出现行标准的任何部分说我做不到.

C++ 03不::bar依赖于名称.依赖类型的类型名称和依赖于类型或值的依赖表达式的非类型名称的依赖关系.如果在依赖类型中查找名称,它将成为依赖于类型的id-expression(14.6.2.2/3 last bullet),并且其查找将延迟到实例化.名称::bar不是这样的依赖表达式.如果你打电话bar(tt),14.2.6的特殊规则C++ 03说

在表达式中:

postfix-expression ( expression-listopt )
Run Code Online (Sandbox Code Playgroud)

其中postfix-expression是标识符,当且仅当表达式列表中的任何表达式是依赖于类型的表达式时,标识符表示从属名称(14.6.2.2).

因此,您需要删除::它以使其成为标识符并使​​其依赖于此特殊规则.

我无法删除::的原因是在我的实际代码中,模板函数foo是类CC的成员函数,并且存在一系列重载的成员函数CC :: bar(...),这意味着我需要限定:: bar(TT*)以避免默认为CC :: bar(...).这就是::存在的,如果标准说我不能在这里使用,我会很惊讶

解决它的正确方法是在函数的局部范围中引入using声明.

namespace dummies { void f(); }
template<typename T>
struct S {
  void f();
  void g() { 
    using dummies::f; // without it, it won't work
    f(T()); // with ::f, it won't work
  }
};

struct A { };
void f(A) { } // <- will find this

int main() {
  S<A> a;
  a.g();
}
Run Code Online (Sandbox Code Playgroud)

如果普通查找找到类成员函数,ADL将不会执行任何操作.因此,您引入了一个using声明,因此普通查找找不到类成员函数,并且ADL可以在实例化时推进声明可见.

但这似乎与你不同意:Stroustrup TC++ PL Sp Ed,C.13.8.1节,从属名称:"基本上,如果一个函数的名称明显依赖于它的参数或者它的参数,它的名称是依赖的形式参数"

Stroustrup的书也是为那些可能还不熟悉C++的人编写的.它不会试图以100%的准确度覆盖所有规则,这对这些书来说是正常的.血淋淋的细节留给了ISO标准读者.

此外,函数的形式参数与函数调用是否依赖无关.在IS中,只有实际参数定义函数名的依赖关系.这与1996年的旧草案不同,后者具有隐性显性依赖的概念.隐含依赖被定义为

如果模板参数是函数调用中使用的函数名,则隐式依赖于模板参数,如果模板参数中提到的类型,模板或枚举器丢失,则函数调用将具有不同的分辨率或无解析来自该计划.

[...]

[示例:一些依赖于模板参数类型T的调用是:

  1. 调用的函数具有一个参数,该参数根据类型推导规则(temp.deduct)依赖于T. 例如,f(T),f(数组)和f(常数T*).

  2. 实际参数的类型取决于T.例如,假设t具有类型T,则f(T(1)),f(t),f(g(t))和f(&t).

还给出了一个实际的例子

这种格式错误的模板实例化使用的函数不依赖于template-argument:

template<class T> class Z {
public:
        void f() const
        {
                g(1); // g() not found in Z's context.
                      // Look again at point of instantiation
        }
};

void g(int);
void h(const Z<Horse>& x)
{
        x.f(); // error: g(int) called by g(1) does not depend
               // on template-argument ``Horse''
}
Run Code Online (Sandbox Code Playgroud)

调用xf()引起了专业化:

void Z<Horse>::f() { g(1); }
Run Code Online (Sandbox Code Playgroud)

调用g(1)会调用g(int),但由于该调用不依赖于模板参数Horse,并且因为g(int)不在模板定义点的范围内,所以调用xf( )是不正确的.

另一方面:

void h(const Z<int>& y)
{
        y.f(); // fine: g(int) called by g(1) depends
               // on template-argument ``int''
}
Run Code Online (Sandbox Code Playgroud)

这里,调用yf()引起了专业化:

void Z<int>::f() { g(1); }
Run Code Online (Sandbox Code Playgroud)

调用g(1)调用g(int),并且因为该调用依赖于tem-plate-argument int,所以调用yf()是可接受的,即使g(int)不在模板点的范围内定义.]

这些东西都留给了历史,甚至它的最后痕迹也在慢慢消失,虽然没有被主动驱动(n3126例如摆脱[temp.names]/p4的"明确依赖"作为另一个变化的副作用,因为"明确依赖"和"隐含依赖"之间的区别在IS中从未存在过.