可变参数模板参数:我可以根据类型选择参考vs值吗? [英] variadic template arguments: can I pick reference vs value depending on type?

查看:160
本文介绍了可变参数模板参数:我可以根据类型选择参考vs值吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

编辑 不是重复 对静态类成员的未定义引用。该问题探讨了问题的原因(我将在下面解释)。这里,我正在寻找一个不同的解决方案从这些问题的答案(暗示更改的声明/定义 constexpr



我创建了一个小的可变参数模板函数 make_string()从任意数量的io-able参数生成 std :: string ,如下所示。

 使用std :: ostringstream; //对于这个例子

inline ostringstream& write(ostringstream& ostr,const char * x)
{if(x)ostr< 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(ostr,x),std :: forward& 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,> max_offset =,
max_offset));
}
};

这会导致链接时出错。原因是 make_string 通过引用获取其所有参数,包括 static constexpr max_offset 。因此,在链接时需要引用 foo :: max_offset 参见



如何避免这个问题,而不放弃 make_string ()? (也许人们可以用一个可变宏来替换可变参数模板,但我认为这是一种回归。)make_string必须有一个方法通过值或引用取决于类型(使内置类型可以取值)。如何?

解决方案

首先,我不知道为什么你需要这么多的代码 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&& ...){}}; 

是一个助手结构,允许以正确的顺序计算表达式href =http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51253 =nofollow>不正确地评估从右到左,直到4.9)






现在,您的问题。正如我在我的评论中说的,我觉得你的问题与 make_string 无关。在静态类成员的未定义引用中,在我的问题通过通用引用传递静态constexpr变量?,在所有相关问题中, ve看到,建议的答案是一个定义变量在某处的类:

  constexpr int foo :: max_offset; 

我不知道这是否对你有问题。这是我的一个问题,因为在模板化的代码中,它意味着太多的重复(见下面的讨论我的问题)。无论如何,如果它是一个问题,我看到一些其他简单的解决方案,以确保call-by-value:




  • use make_string(...,max_offset)而不是 make_string = $ http:// stackoverflow。


  • 作为快捷方式, + max_offset com / a / 272996/2644390>这里)


  • define static constexpr int max_offset(){return some_value; } ,然后使用 max_offset(),而不是 max_offset li>

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


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




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


edit This is not a duplicate of Undefined reference to static class member. That question explored the cause of the problem (which I explain below). Here, I'm looking for a different solution from those proposed in the answers to that questions (which implied changing the declaration/definition of the constexpr variable to be used -- essentially by adding a definition in a compilation unit).

I have created a little variadic template function make_string() to generate a std::string from any number of io-able arguments as follows.

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());
}

Now, this works pretty well and can be used like this

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

However, there is a problem when printing static constexpr class members, as in

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"));
   }
};

This causes an error at link time. The reason is that make_string takes all its arguments by reference, including the static constexpr max_offset. As a result, a reference to foo::max_offset will be required at linking, see also.

How can I avoid this problem without abandoning the idea of make_string()? (Perhaps one could replace the variadic template with a variadic macro, but I would consider this as some sort of regression.) There must be a way for make_string to take its arguments by value or reference, depending on type (so that builtin types can be taken by value). How?

解决方案

First, I am not sure why you need so much code for make_string. I'd simply define it as

template<class... Args>
inline std::string make_string(Args&&... args)
{
  ostringstream ostr;
  _do{ostr << std::forward<Args>(args)...};
  return std::move(ostr.str());
}

where

struct _do { template <typename... T> _do(T&&...) { } };

is a helper struct that lets you evaluate expressions in the right order (but watch out, GCC incorrectly evaluates right-to-left until 4.9 at least).


Now, to your question. As I said in my comment, I feel your problem is irrelevant to make_string. In Undefined reference to static class member, in my question passing a static constexpr variable by universal reference?, and in all relevant questions I've seen, the suggested answer is that one defines the variable somewhere out of class:

constexpr int foo::max_offset;

I'm not sure if this is a problem for you. It is a problem for me because in heavily templated code it implies too much duplication (see discussion below my question). Anyhow, if it is a problem, I see a few other simple solutions to ensure call-by-value:

  • use make_string(..., int(max_offset)) instead of make_string(..., max_offset)

  • as a shortcut, +max_offset does the same job (suggested here)

  • define static constexpr int max_offset() { return some_value; }, then use max_offset() instead of max_offset throughout

  • let some part of code (function or template) deduce max_offset as a non-type int template parameter, then use it directly

  • lastly, define make_string(Args... args) (this is the simplest but does not apply here as you don't want to copy all those strings)

I am not discussing use of make_string in throwing an exception; this is a different problem.

这篇关于可变参数模板参数:我可以根据类型选择参考vs值吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆