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

标签 c++ clang

我一直在尝试使用 Sean Parent 的“C++ Seasoning”演示文稿派生的代码,并将我的问题归结为以下代码:

#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);
}

我遇到的问题是这段代码是用 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)
     ^

我的理解是,模板期间的重载决策发生在实例化时。在本例中,即第 34 行(如上标记)。此时,全局“foo”函数是已知的。然而,它似乎没有解决。

后代注意事项:这是 Clang 在 14/Jan/14 从主干构建的

那么这是 Clang 中的错误,还是 g++ 中的错误?

最佳答案

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

考虑简化的例子:

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 类中的 friend 声明 [1] 为命名空间级函数 f 提供了声明,该函数采用 int但是 在 [4] 中出现命名空间级声明之前,该声明在命名空间级不可见。 [2] 和 [3] 都将无法编译,尽管 [5] 会编译,因为此时编译器已经解析了函数声明。

那么编译器如何使用[1]中的声明来解析调用呢?在这种特殊情况下从不。友元声明只能通过参数相关查找找到,但如果函数调用的参数之一是 X 类型,ADL 只会在 X 内部查找。在这种情况下,该函数没有任何参数 X,因此查找将从不使用 friend 声明来解除限制访问 X 的变量。

即:

struct Y {
   friend void f(int) {}
};

如果 f 没有后面的 namespace 级别声明,将声明和定义一个不能在程序中的任何地方使用的函数(查找永远找不到它)。

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

#include <memory>

void foo(int);
struct container { // ...

关于c++ - Clang 在模板上下文中找不到函数定义后实例化的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21115247/

相关文章:

c++ - Xcode 4.3 和 C++11 包含路径

c++ - 复制构造函数和转发构造函数之间的冲突

c++ - 使用虚拟继承的类似乎允许基类构造函数覆盖另一个基类的成员

c++ - 强制单参数构造函数在 C++ 中显式?

c++ - 我可以在循环 vector<shared_ptr<BaseClass>> 时以某种方式调用派生类方法吗?

c++ - 如何使用valgrind?

c - 哪个 C99 编译器(Clang 与 GCC)更接近 const 结构字段的标准?

与 stdarg 相关的 clang 错误?

c++ - vector C++ 中的非重复随机数

c++ - 在 C++ 中删除 vector 的单个元素的问题