避免对不同的char数组大小进行模板实例化

时间:2015-04-02 16:36:15

标签: c++ templates variadic-templates

我有一个简单的可变参数模板代码,用于将参数写入流:

#include <iostream>

void tostream(std::ostream& os) {
}

template<typename T, typename... Args>
void tostream(std::ostream& os, const T& v, const Args&... args) {
    os << v;
    tostream(os, args...);
}

template<typename... Args>
void log(std::ostream& os, const Args&... args) {
    tostream(os, args...);    
}

我可以打电话:

log(std::cout, "Hello", 3);  
log(std::cout, "Goodbye", 4);

我使用Visual Studio 2013编译此代码并进行所有优化(Release config)并使用IDA反汇编程序打开生成的可执行文件。
我看到的是编译器实例化了log()函数的两个副本。一个需要const char[6], int,一个需要const char[8], int 当调试这些函数并观察调用堆栈窗口时,这在调试器中也很明显 除了签名之外,这两个功能是相同的。

有没有办法让编译器相信这两个函数实际上应该是一个需要const char*, int而不是两个函数的函数?

这对我来说是一个问题,因为我有数百个这样的函数实例,这些实例化的大小与我的可执行文件一样大,其中大部分都可以避免。
对于参数的不同组合,函数仍然会有很多实例化,但由于我只有极少数可能的参数组合,所以它们会少得多。

解决这个问题的一件事就是像这样调用函数:

log(cout, (const char*)"Hello", 3);  
log(cout, (const char*)"Goodbye", 4);

但这是不可接受的,因为它会大大混乱代码。

2 个答案:

答案 0 :(得分:6)

 template<class T>
 using decay_t = typename std::decay<T>::type;

 template<typename... Args>
 void log(ostream& os, const Args&... args) {
   tostream(os, decay_t<Args>(args)...);    
 }

会在传递参数之前手动衰减它们tostream。这会将函数转换为函数指针,将数组引用转换为指针等等。

这可能会导致一些虚假副本。对于原始类型,没有问题,但对std::string等是浪费的。所以一个更狭窄的解决方案:

template<class T>
struct array_to_ptr {
  using type=T;
};
template<class T, size_t N>
struct array_to_ptr<T(&)[N]> {
  using type=T*;
};
template<class T>
using array_to_ptr_t=typename array_to_ptr<T>::type;

template<typename... Args>
void log(ostream& os, const Args&... args) {
  tostream(os, array_to_ptr_t<Args const&>(args)...);    
}

只会对数组执行此操作。

请注意,log的不同实现可能仍然存在,但不存在tostreamlog的独特实现应该通过comdat折叠和/或内联log来消除,并且可能消除递归要求(注意它是可折叠的)将使其更容易。

最后,这可能很有用:

template<typename... Args>
void tostream(std::ostream& os, const Args&... args) {
  using expand=int[];
  (void)expand{0,
    ((os << args),void(),0)...
  };
}

直接扩展而不对一个函数中的参数进行递归。你的编译器应该足够聪明,可以发现0 s的隐含数组是无用的,即使不是,与io相比,开销也很小。

答案 1 :(得分:1)

我没有为您提供适当的解决方案,但就解决方法而言,以下内容可能会更少,而且#34;混乱&#34;为你?

log(cout, +"Hello", 3);  
log(cout, +"Goodbye", 4);

我感谢您仍然需要您的用户记住这样做,这很糟糕。

相关问题