C++ 可变模板参数:我可以根据类型选择引用与值吗?
编辑此项不是的重复项。这个问题探讨了问题的原因(我在下面解释)。在这里,我正在寻找一种与这些问题的答案中提出的解决方案不同的解决方案(这意味着更改要使用的C++ 可变模板参数:我可以根据类型选择引用与值吗?,c++,templates,c++11,variadic-templates,argument-passing,C++,Templates,C++11,Variadic Templates,Argument Passing,编辑此项不是的重复项。这个问题探讨了问题的原因(我在下面解释)。在这里,我正在寻找一种与这些问题的答案中提出的解决方案不同的解决方案(这意味着更改要使用的constexpr变量的声明/定义——基本上是通过在编译单元中添加定义) 我创建了一个小的可变模板函数make_string(),从任意数量的可输入输出参数生成std::string,如下所示 using std::ostringstream; // just for this example inline ostringstream&
constexpr
变量的声明/定义——基本上是通过在编译单元中添加定义)
我创建了一个小的可变模板函数make_string()
,从任意数量的可输入输出参数生成std::string
,如下所示
using std::ostringstream; // just for this example
inline ostringstream&write(ostringstream&ostr, const char*x)
{ if(x) ostr<<x; return ostr; }
template<class T>
inline ostringstream&write(ostringstream&ostr, T const&x)
{ ostr<<x; return ostr; }
inline ostringstream&write(ostringstream&ostr) noexcept
{ return ostr; }
template<class T, class... R>
inline ostringstream&write(ostringstream&ostr, T const&x, R&&... r)
{ return write(write(ostr,x), std::forward<R>(r)...); }
inline std::string make_string(const char*text)
{ return {text?text:""}; }
inline std::string make_string(std::string const&text)
{ return {text}; }
template<typename T>
inline auto make_string(T var) -> decltype(std::to_string(var))
{ return std::to_string(var); }
template<class... Args>
inline std::string make_string(Args&&... args)
{
ostringstream ostr;
write(ostr,std::forward<Args>(args)...);
return std::move(ostr.str());
}
但是,在打印静态constexpr
类成员时会出现问题,如中所示
class foo
{
static constexpr int max_offset=some_value;
// ...
void bar(int offset)
{
if(offset > max_offset)
throw std::runtime_error(make_string("offset=",offset," > max_offset=",
max_offset"));
}
};
这会在链接时导致错误。原因是make_string
通过引用获取其所有参数,包括static constexpr
max_offset
。因此,链接时需要引用foo::max_offset
我如何避免这个问题而不放弃
make_string()
?(也许可以用一个变量宏替换变量模板,但我会认为这是某种回归。)必须有一种方法来让MaxStand根据值或引用来根据其类型来进行参数化(这样,构建类型可以通过值来取值)。怎么做?我不确定编译器是否正确地将它的内裤放在一堆jimmies中,jimmies在这里引用了constexpr
然而,您也许可以使用boost的
定义一个类型,该类型表示将T类型的参数传递给函数的“最佳”方式call_traits::param_type
(请参阅)。首先,我不知道为什么您需要这么多代码来制作
字符串。我只是把它定义为
template<class... Args>
inline std::string make_string(Args&&... args)
{
ostringstream ostr;
_do{ostr << std::forward<Args>(args)...};
return std::move(ostr.str());
}
我不确定这对你来说是否是个问题。这对我来说是一个问题,因为在大量模板化的代码中,它意味着太多的重复(请参阅我问题下面的讨论)。无论如何,如果这是一个问题,我会看到一些其他简单的解决方案来确保按值调用:
- 使用
生成字符串(…,int(最大偏移量))
而不是生成字符串(…,最大偏移量)
- 作为快捷方式,
+max_offset
执行相同的操作(建议)
- 定义
static constexpr int max_offset(){返回一些值;}
,然后使用max_offset()
而不是max_offset
- 让代码的某些部分(函数或模板)推断出
max_offset
作为非类型int
模板参数,然后直接使用它
- 最后,定义
生成字符串(Args…Args)
(这是最简单的,但不适用于此处,因为您不想复制所有这些字符串)
我没有讨论在抛出异常时使用make_string
;这是另一个问题。可能重复@MarkGarcia,但情况并非如此。我不想知道问题的原因,但我想问的是,这个问题是否可以用回答这个问题时没有探讨过的某种方式来解决。你在用什么编译器?最近的clang和gcc对此没有问题。@jrok是的,我使用的是clang 3.4和gcc 4.8.1,但有一个比上面更复杂的示例,分布在多个编译单元上。的可能重复。特别是,我认为整个make_string
应用程序是不相关的,问题可以通过更简单的代码表现出来。请注意,选择的答案不是我的首选解决方案,如下所述。还有其他的解决方案,我可以在几个小时内尝试一个答案,并提供一些更好的make_string
(现在开始)。我不理解你的最后一段。在我的代码中,在创建std::string
时只实例化了一个(不是很多)ostringstream
。我的函数write()
完全按照您所说的做:返回ostringstream
。此外,当抛出异常时,您很可能不会重用任何东西。很抱歉,我忽略了make\u string
和write
之间的区别。你说得对。但是,从概念上讲,当异常正在运行时,正好是避免执行分配的时候(这就是为什么std::exception::what()
是no throw并返回const char*
Yes,但是如果不是这样的话,如何组合一个完整的错误消息呢?可以使用一些threadlocal静态保留内存和snprintf
--这是您的建议吗?或者使用threadlocal静态初始化的stringstream(正确地保留足够的缓冲空间):)我知道,您必须懒洋洋地构造它(这就是我在代码库中所做的)。(我实际上删除了最后一段,我忘了——有点忙)听起来很有趣。我不明白的是你是如何懒散地构建它的。你的意思是你的(相当于)make_string
而不是启动一个新的ostringstream
简单地clear()。目前,我将使用您的+max\u offset
解决方案。我对你的结构感兴趣。这种魔法是从哪里来的?是否有相关的在线讨论/文档?你不能用GCC“修复”这个bug吗(@Walter公平地说,我看到了一元+
解决方案(答案中更新了链接)。现在,\u do
:我第一次看到它是在熟悉时(查找结构传递
),然后又是在STL实现中,通常称为swallow
(如GCC bug报告中所示)。这是基于在列表初始化中,参数按外观顺序(从左到右)计算的要求。我知道的唯一文档是。@Walter至于GCC错误,可能我不清楚,但我的意思是(不幸的是)该错误仍然存在于4.9中。我不知道更多,也不容易找到(请尝试)
template<class... Args>
inline std::string make_string(Args&&... args)
{
ostringstream ostr;
_do{ostr << std::forward<Args>(args)...};
return std::move(ostr.str());
}
struct _do { template <typename... T> _do(T&&...) { } };
constexpr int foo::max_offset;