C++ 为什么很清楚模板函数实例化不会内联?

C++ 为什么很清楚模板函数实例化不会内联?,c++,templates,C++,Templates,关于这一点,Ben Supnik提供的社区wiki答案讨论了内联实例化函数模板的问题 该答案中包含以下代码: template<typename OP> int do_op(int a, int b, OP op) { return op(a,b,); } int add(int a, b) { return a + b; } int (* func_ptr)(int, int) = add; int c = do_op(4,5,func_ptr); 模板 int do_

关于这一点,Ben Supnik提供的社区wiki答案讨论了内联实例化函数模板的问题

该答案中包含以下代码:

template<typename OP>
int do_op(int a, int b, OP op)
{
  return op(a,b,);
}

int add(int a, b) { return a + b; }

int (* func_ptr)(int, int) = add;

int c = do_op(4,5,func_ptr);
模板
int do_op(int a,int b,op)
{
返回op(a,b,);
}
intadd(inta,b){返回a+b;}
int(*func_ptr)(int,int)=添加;
int c=do_op(4,5,func_ptr);
答案是这样的(关于最后一行,它实例化了函数模板
do_op
):

显然,这不是内联的

我的问题是:为什么这显然不是内联的?

他(我想)的意思是
add
函数没有内联。换句话说,编译器可以内联
执行以下操作:

int c = func_ptr(4, 5);
int c = 4 + 5;
但它也不会像这样内联
添加

int c = func_ptr(4, 5);
int c = 4 + 5;
然而,在这个简单的例子中,他可能是错的

通常,当您通过指针调用函数时,编译器无法知道(在编译时)将调用什么函数,因此无法内联函数。例如:

void f1() { ... }
void f2() { ... }

void callThroughPointer() {
    int i = arc4random_uniform(2);
    void (*f)() = i ? f2 : f1;
    f();
}
void f1() { ... }
void f2() { ... }

void callThroughPointer2() {
    int i = arc4random_uniform(2);
    void (*f)() = i ? f2 : f1;
    f = f1;
    f();
}
在这里,编译器无法知道
callThroughPointer
是否将调用
f1
f2
,因此它无法在
callThroughPointer
中内联
f1
f2

但是,如果编译器可以在编译时证明将调用什么函数,则允许它内联函数。例如:

void f1() { ... }
void f2() { ... }

void callThroughPointer() {
    int i = arc4random_uniform(2);
    void (*f)() = i ? f2 : f1;
    f();
}
void f1() { ... }
void f2() { ... }

void callThroughPointer2() {
    int i = arc4random_uniform(2);
    void (*f)() = i ? f2 : f1;
    f = f1;
    f();
}
在这里,编译器可以证明
f
将始终是
f1
,因此允许将
f1
内联到
调用指针2
。(这并不意味着它将内联
f1
。)

类似地,在您在文章中引用的示例中,编译器可以证明
func_ptr
在调用
do_op
时总是
add
,因此允许内联
add
。(这并不意味着它将内联
添加
。)

为什么这不是内联的


不是。编译器没有理由不能内联该代码段中的所有代码。

当通过函数指针调用函数时,编译器不太可能通过函数指针避免调用。只有当编译器能够证明它知道函数指针是用什么初始化的,并且不能更改时,它才有可能避免通过函数指针调用函数,从而内联函数。在引用的设置中,即

int (* func_ptr)(int, int) = add;
函数指针
func_ptr
是可修改的,因此不保证编译器永远不会更改。因此,它不可能内联调用
add


如果代码片段确实是完整的,那么在初始化过程中会发生一些事情,编译器实际上可以知道
func_ptr
被初始化为包含
add

,我认为,到目前为止,这里的讨论忽略了要点。首先,由于语法错误,测试代码甚至无法编译。可能是指:

template<typename OP>
int do_op(int a, int b, OP op) { return op(a,b); }
int add(int a, int b) { return a + b; }
int (*func_ptr)(int, int) = add;
int c = do_op(4, 5, func_ptr);
// int c = (*func_ptr)(4, 5);

因此,总结一下:通过模板调用函数不会阻止优化器完成其工作。如果在编译时无法安全地确定函数指针的值,那么函数指针可能会造成危害,但如果添加了模板,问题不会恶化。

没有理由不这样做,但有很多理由它甚至可能不尝试这样做。如果我没有被误会的话,那么直到最近,C(和C++)编译器甚至都没有为了内联的目的尝试跟踪函数指针。@KonradRudolph-当然,但声明是很明显这不是内联的。一点也不清楚。