可变参数模板参数:我可以根据类型选择参考值和值吗?

时间:2014-03-14 09:31:33

标签: c++ templates c++11 variadic-templates argument-passing

编辑 Undefined reference to static class member 不是重复的。这个问题探讨了问题的原因(我在下面解释)。在这里,我正在寻找与那些问题的答案中提出的不同的解决方案(这意味着更改要使用的constexpr变量的声明/定义 - 基本上通过在编译单元中添加定义。)

我创建了一个小的可变参数模板函数make_string(),可以从任意数量的io-able参数生成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());
}

现在,这非常有效,可以像这样使用

throw std::runtime_error(make_string("offset=",offset," > max_offset =",
                                      max_offset"));

但是,打印static 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。因此,在see also链接时需要引用foo::max_offset

如果不放弃make_string()的想法,我该如何避免这个问题? (也许可以使用可变参数宏替换可变参数模板,但我会将其视为某种回归。)make_string必须有一种方法可以通过值或引用来获取其参数,具体取决于类型(以便内置类型可以取值)。怎么样?

2 个答案:

答案 0 :(得分:3)

我不确定编译器是否正确使用它的短裤一串 jimmies在这里引用了constexpr。

然而,你或许可以找到使用boost的方法

  • call_traits<T>::param_type

    定义一个代表&#34; best&#34;将类型T的参数传递给函数的方法。

(见http://www.boost.org/doc/libs/1_55_0/libs/utility/call_traits.htm)。

答案 1 :(得分:2)

首先,我不确定为什么你需要make_string这么多的代码。我只是将其定义为

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&&...) { } };

是一个帮助程序结构,它允许您按正确的顺序计算表达式(但请注意,GCC incorrectly从右到左计算,直到4.9为止。)


现在,问你的问题。正如我在评论中所说,我觉得你的问题与make_string无关。在Undefined reference to static class member中,在我的问题passing a static constexpr variable by universal reference?中,以及我已经看过的所有相关问题中,建议的答案是在课外某处定义变量:

constexpr int foo::max_offset;

我不确定这对你来说是否有问题。对于我来说 是一个问题,因为在严格模板化的代码中,它意味着过多的重复(请参阅我的问题下面的讨论)。无论如何,如果这是一个问题,我会看到一些其他简单的解决方案来确保按值调用:

  • 使用make_string(..., int(max_offset))代替make_string(..., max_offset)

  • 作为一种捷径,+max_offset执行相同的工作(建议here

  • 定义static constexpr int max_offset() { return some_value; },然后使用max_offset()代替max_offset

  • 让代码的一部分(函数或模板)将max_offset推导为非类型int模板参数,然后直接使用

  • 最后,定义make_string(Args... args)(这是最简单但不适用于此,因为您不想复制所有这些字符串)

我不讨论使用make_string抛出异常;这是一个不同的问题。