C++ Clang在模板上下文中未找到函数定义后实例化的函数

C++ Clang在模板上下文中未找到函数定义后实例化的函数,c++,clang,C++,Clang,我一直在试验Sean Parent的“C++调味”演示文稿中衍生的代码,并将我的问题归结为以下代码: #include <memory> struct container { struct concept { virtual ~concept() {} virtual void foo_() = 0; }; template <class T> struct model : concept { m

我一直在试验Sean Parent的“C++调味”演示文稿中衍生的代码,并将我的问题归结为以下代码:

#include <memory>

struct container {
    struct concept {
        virtual ~concept() {}
        virtual void foo_() = 0;
    };

    template <class T> struct model : concept {
        model (T x) : data_(x) {}

        void foo_() {
            foo(data_); // Line 13
        }

        T data_;
    };

    template <class T>
    container(T x) : self_(new model<T>(x)) {} // Line 20

    std::unique_ptr<concept> self_;

    friend void foo(container &c) { c.self_->foo_(); }
};

void foo(int i) // Line 27
{
}

int main()
{
    int i = 5;
    container c(i); // Line 34
    foo(c);
}
#包括
结构容器{
结构概念{
虚拟~concept(){}
虚拟void foo_uz()=0;
};
模板结构模型:概念{
模型(tx):数据_ux{}
void foo_uz(){
foo(数据);//第13行
}
T数据;
};
模板
容器(tx):self(新模型(x)){}//第20行
std::唯一的\u ptr self\uu;
friend void foo(container&c){c.self->foo()}
};
void foo(int i)//第27行
{
}
int main()
{
int i=5;
容器c(i);//第34行
傅(丙),;
}
我的问题是,这段代码是用g++编译的,而不是用Clang

Clang向我提供了以下错误消息:

prio.cpp:13:13: error: call to function 'foo' that is neither visible in the
      template definition nor found by argument-dependent lookup
            foo(data_);
            ^
prio.cpp:20:32: note: in instantiation of member function
      'container::model<int>::foo_' requested here
    container(T x) : self_(new model<T>(x)) {}
                               ^
prio.cpp:34:15: note: in instantiation of function template specialization
      'container::container<int>' requested here
    container c(i);
              ^
prio.cpp:27:6: note: 'foo' should be declared prior to the call site
void foo(int i)
     ^
prio.cpp:13:13:错误:对函数“foo”的调用在
模板定义未通过参数相关查找找到
foo(数据);;
^
prio.cpp:20:32:注意:在成员函数的实例化中
此处请求了“container::model::foo_”
容器(tx):self_(新模型(x)){}
^
prio.cpp:34:15:注意:在函数模板专门化的实例化中
此处请求了“container::container”
集装箱c(i);
^
prio.cpp:27:6:注意:“foo”应该在调用站点之前声明
空富(内部一)
^
我的理解是,模板期间的重载解析发生在实例化点。在本例中,即第34行(如上所示)。此时,全局“foo”函数是已知的。然而,它似乎没有解决

后人注意:这是2014年1月14日用树干建造的铿锵


这是Clang中的一个bug,还是g++?

Gcc是错误的,在这种情况下,代码不应该编译;但这与模板完全无关。友元声明的特殊之处在于,它们为名称空间级别的实体提供了一个声明,但在编译器也看到名称空间声明之前,该声明对于正常查找是不可见的

考虑简化的示例:

struct X {
   friend void f(int);   // [1]
   void g() { f(1); }    // [2]
};
void h() { f(1); }       // [3]
void f(int);             // [4]
void i() { f(1); }       // [5]
X
类中的友元声明[1]为命名空间级函数
f
提供了一个声明,该声明接受
int
但在[4]中出现命名空间级声明之前,该声明在命名空间级不可见。[2]和[3]都将无法编译,尽管[5]将编译,因为此时编译器将解析函数声明

那么编译器如何使用[1]中的声明来解析调用呢?在这种特殊情况下,永远不会。友元声明只能通过依赖于参数的查找找到,但如果函数调用的某个参数的类型为
X
,ADL将只查看
X
。在这种情况下,函数没有任何参数
X
,因此lookup将永远不会使用
friend
声明来执行任何操作,除非解除对
X
变量的访问限制

即:

struct Y {
   friend void f(int) {}
};
如果没有后面的
f的命名空间级声明,则
将声明和定义一个不能在程序中任何地方使用的函数(查找永远找不到它)

解决问题的简单方法是在定义类之前在命名空间级别为函数提供声明:

#include <memory>

void foo(int);
struct container { // ...
#包括
void foo(int);
结构容器{//。。。

这是g++的一个扩展。名称
foo
foo(data\ux)中
是一个依赖名称,因为
数据依赖于模板参数。因此依赖于参数的查找将推迟到实例化点。但是,
int
的依赖于参数的查找将找不到任何函数,因为没有与基本类型关联的命名空间和类。@dyp Ok,但即使我更改了e foo(int)到foo(std::vector)(以及main中的相应变量),错误仍然是一样的(int被std::vector替换)。或者我完全误解了你所说的吗?我也尝试过放foo()在一个名称空间中,并适当地调用它,但是g++和clang都不接受它,都退出了,甚至不需要实例化模型。@dyp:您在注释中提到的大部分内容都是正确的,尽管我不确定结论是否正确。查找推迟到第二阶段的事实并不意味着只有ADL将使用。在第二阶段解析的标识符将在实例化时在模板定义和ADL的上下文中查找。这里的问题是是否应该编译:
struct T{voif f(){g(1);};friend void g(int);};
(注意,我完全删除了模板)。gcc和clang都拒绝这样做,尽管我不确定他们是否应该…@KazDragon注意到Sean Parent在他的演讲中使用了用户定义的类型。也就是说,他没有将
int
放在容器中,而是将一些
结构文档{/*…*/};
放在容器中(IIRC).ADL只是不查找基本类型,例如
int
,它只查找
名称空间std
(可靠)对于
std::vector
。在所有这些之后,我不确定它是否相关,但在我的示例中,friend函数采用的是X,而不是int。类外的foo不是befriended函数;只是一个外观类似的函数。实际上,friend函数foo(container)但是,这可能是正确的。我注意到将foo(int)更改为foo(vector)(并添加适当的移动/前进)确实有效。@KazDragon:相同的区别,在使用函数之前,您确实需要提供函数声明。好的,我刚刚测试过,如果您在名称空间中创建UDT并使用它,则是的