<Inside The C++ Object Model> 中的模板分辨率

Template Resolution in <Inside The C++ Object Model>

在"Inside the C++ Object Model,"的第7章中写到,nomember name的解析取决于name的使用是否与"the type of parameter used to instantiate the template."相关 我写一个测试:

/// -------------Test.h---------------
#ifndef TEST_H
#define TEST_H

#include <iostream>

using namespace std;

extern double foo(double t);

template <typename T>
class Test {
    public:
        void fun1() {
            member = foo(val);
        }
        T fun2() {
            return foo(member);
        }
    private:
        int val;
        T member;
};

#endif

/// -------------test1.cc-------------
#include <iostream>

using namespace std;

double foo(double t) {
    cout << "foo doule is called" << endl;
    return t;
}

int foo(int t) {
    cout << "foo int is called" << endl;
    return t;
}
-------------test.cc--------------
#include "Test.h"

extern int foo(int t);

int main() {
    Test<int> fi;
    fi.fun1();
    fi.fun2();
    return 0;
}

我希望“foo double 是 called\n foo int 被称为", 但我得到“foo double is called\n foo double 被称为“。 我的 g++ 版本如下。如果你能帮助我,我将不胜感激。

恐怕这本书没有描绘出完整的画面(而且有点陈旧)。是的,foo(member) 是依赖于模板参数的函数调用。但是在模板中查找函数的具体方式在 C++ 标准中有描述 [temp.dep.candidate]:

For a function call where the postfix-expression is a dependent name, the candidate functions are found using the usual lookup rules ([basic.lookup.unqual], [basic.lookup.argdep]) except that:

  • For the part of the lookup using unqualified name lookup, only function declarations from the template definition context are found.
  • For the part of the lookup using associated namespaces ([basic.lookup.argdep]), only function declarations found in either the template definition context or the template instantiation context are found.

foo 的重载可以通过以下两种方式之一查找。通过直接不合格查找和 argument dependent lookup(又名 ADL)。简单的非限定查找仅考虑在模板定义点已知的名称。由于您只声明了 foo(double),这是在模板定义点发现的唯一重载。

在实例化时,编译器 尝试执行 ADL 以查找更多 foo,但基本类型对 ADL 没有贡献。 int 不能用于查找 foo(int)。所以编译器只能做一件事,将整数转换为双精度数,然后调用 foo(double).

如果您想测试您的编译器 ADL,您只需添加一个简单的用户定义类型和重载。例如:

enum E{};
E foo(E) {
  cout << "foo E is called\n";
  return {};
}

int main() {
    Test<E> fi;
    fi.fun1();
    fi.fun2(); 
    return 0;
}