2012-01-26 55 views
12

我想写一个模板函数,它与std::stack<T>东西和T一个实例,如:一个好的C++编译器会优化一个引用吗?

template<class StackType> inline 
bool some_func(StackType const &s, typename StackType::value_type const &v) { 
    // ... 
} 

我按引用传递v原因当然是优化的情况下StackType::value_typestructclass,而不是按值复制整个对象。

但是,如果StackType::value_type是像“int”这样的“简单”类型,那么它当然更好。

现在的问题是:对于int这样的类型,在上面的函数中会变成int const&作为形式参数,编译器是否会优化掉引用并仅通过值传递呢?

+2

这将是一个糟糕的编译器。 – lapk

+0

如果编译器实际上决定内联函数,无论您如何定义它,它肯定会做最有效的事情。 – enobayram

回答

6

虽然我没有真正测试过任何编译器,但我对此表示怀疑。你假设传递一个const引用与传递一个值是无法区分的,但事实并非如此,编译器不应该假设它是。

由于引用是const,因此函数不能通过它修改值,但代码的其他部分可以访问原始(非const)变量,并且可以修改它。例如,你的函数可能会调用其他一些函数来改变它,或者可能会有另一个线程同时运行。

如果其他内容修改了原始变量,则您的函数及其引用应该会看到新值。如果编译器用一个副本替换引用,该函数仍然会看到旧值。

虽然你可能会对Boost的call traits库感兴趣。它提供了一个模板类型call_traits<T>::param_type,它是您不想复制的“大”类型的常量引用,以及“副”更有效的“小”类型的值。基本上,你希望编译器隐含地做什么,你可以明确地使用你的代码。

+3

你对线程不正确。编译器优化器经常会破坏代码,这些代码与糟糕的“无锁”(天真)数据结构一起工作。编译器需要考虑其他线程! –

+0

此外,简单函数根本不会调用其他函数 –

+0

@AlexandrPriymak,该问题未指定函数中的代码,因此它可能是调用其他函数的代码。而且C++ 11包含一个并发感知内存模型和标准化的线程工具,所以C++ 11编译器必须考虑其他线程可能做什么,即使C++ 98编译器不这样做。 – Wyzard

6

只要编译器能够内联,产生的跨程序优化将消除查找和传递地址的开销。

对于非内联函数调用,引用可能会作为指针实现,而不是按值传递。

3

这是MSVC2010 64位的编译器,全优化:

int foo(int i) 
{ 
int a = i + i; 

return (a); 
} 
//Disassembly: 
//00000001`3ff11c50 8d0409   lea  eax,[rcx+rcx] 
//00000001`3ff11c53 c3    ret 

int bar(int const & i) 
{ 
int a = i + i; 

return (a); 
} 
//Disassembly: 
//00000001`3ff11c10 8b01   mov  eax,dword ptr [rcx] 
//00000001`3ff11c12 03c0   add  eax,eax 
//00000001`3ff11c14 c3    ret 

在第一种情况,值被传递RCX,在第二 - 上RCX传递地址。当然,如果函数是内联的,就像Ben说的那样,那么在两种情况下生成的代码可能都是完全相同的。

传递“小”类(例如,类只有一个int数据成员和普通的构造函数,按位拷贝构造函数等)的实例通过值更快。编译器会将其优化为基本上只传递int的副本。所以,你可以使用一些类型特征来重载你的函数,就像Wyzard指出的那样。类似的东西:

template< typename PType > 
PType Func(typename std::enable_if< (sizeof(PType) > sizeof(size_t)), PType const & >::type f) 
{ 
//large things: reference 
} 

template< typename PType > 
PType Func(typename std::enable_if< (sizeof(PType) <= sizeof(size_t)), PType >::type f) 
{ 
//small things: value 
} 
+1

请注意,您的实验分析适用于“正常”功能,而不适用于模板化功能。这是一个关键的区别;普通函数不能(一般情况下)通过编译器优化来调整其参数列表,而模板化函数必须是可以的并且因此可以。 –

+0

@BrooksMoses函数内联不依赖于它是模板化的还是非模板化的函数。简单地说,人们在每个编译单元中使用函数定义的模板,并不能保证其内联......如果我的“实验”函数在一个编译单元中同时声明和定义,它们将被内联。代码将是相同的。反过来,即使是“简单的”模板化函数也可能不会被内联。 – lapk

+0

是的,我过于简单化了。尽管如此,如果您的“实验”函数是在单个编译单元中声明和定义的,但是声明的方式是从该函数单元导出的,那么它们不能被内联。而且你没有表明它们是静态的,或者你正在编写一个独立的整体程序,这意味着它们必须被导出。 –

6

我期待在这里gcc的优化选项http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html

而且居然还有您的情况选择:

-fipa-SRA

进行进程标更换集合,删除未使用的参数以及通过按值传递的参数替换通过引用传递的参数。

启用在级别-O2,-O3和-Os

据我所知,-02是在Linux发行版本常见的选项。

所以,简单的答案是:编译好的一个确实

相关问题