Why does this template function not behave as expected?

Zho*_*eng 17 c++ dependent-name function-templates name-lookup unqualified-name

I was reading about template functions and got confused by this problem:

#include <iostream>

void f(int) {
    std::cout << "f(int)\n";
}

template<typename T>
void g(T val) {
    std::cout << typeid(val).name() << "  ";
    f(val);
}

void f(double) {
    std::cout << "f(double)\n";
}

template void g<double>(double);

int main() {
    f(1.0); // f(double)
    f(1);   // f(int)
    g(1.0); // d  f(int), this is surprising
    g(1);   // i  f(int)
}
Run Code Online (Sandbox Code Playgroud)

The results are the same if I don't write template void g<double>(double);.

I think g<double> should be instantiated after f(double), and therefore the call to f in g should call f(double). Surprisingly, it still calls f(int) in g<double>. Can anyone help me understand this?


After reading the answers, I figured out what my confusion really is.

Here is an updated example. It is mostly unchanged except that I added a specialization for g<double>:

#include <iostream>

void f(int){cout << "f(int)" << endl;}

template<typename T>
void g(T val)
{
    cout << typeid(val).name() << "  ";
    f(val);
}

void f(double){cout << "f(double)" << endl;}

//Now use user specialization to replace
//template void g<double>(double);

template<>
void g<double>(double val)
{
    cout << typeid(val).name() << "  ";
    f(val);
}

int main() {
    f(1.0); // f(double)
    f(1);  // f(int)
    g(1.0); // now d  f(double)
    g(1);  // i  f(int)
}
Run Code Online (Sandbox Code Playgroud)

With the user specialization, g(1.0) behaves as I expected.

Should the compiler not automatically do this same instantiation for g<double> in the same place (or even after main(), as described in section 26.3.3 of The C++ Programming Language, 4th edition)?

Evg*_*Evg 8

名称f是一个从属名称(它T通过参数依赖val),它将分解为两个步骤

  1. 非ADL查找检查从模板定义上下文可见的函数声明。
  2. ADL检查从模板定义上下文模板实例化上下文可见的函数声明。

void f(double)在模板定义上下文中不可见,并且ADL也不会找到它,因为

对于基本类型的参数,关联的名称空间和类的集合为空


我们可以稍微修改一下您的示例:

struct Int {};
struct Double : Int {};

void f(Int) { 
    std::cout << "f(Int)";
}

template<typename T>
void g(T val) {
    std::cout << typeid(val).name() << ' ';
    f(val);
    // (f)(val);
}

void f(Double) { 
    std::cout << "f(Double)";
}

int main() {
    g(Double{});
}
Run Code Online (Sandbox Code Playgroud)

现在,ADL将void f(Double)在第二步中找到,输出将是6Double f(Double)。我们可以通过写(f)(val)(或::f(val))而不是禁用ADL f(val)。然后输出将6Double f(Int)与您的示例一致。


小智 5

The problem is f(double) has not been declared at the point where you call it; if you move its declaration in front of the template g, it will get called.