Variadic template arguments: can I choose a link vs value depending on type?

edit This is not a duplicate of Undefined references to a static member of a class . The issue has been addressed in this issue. problems (which I explain below). Here I am looking for another solution from those proposed in the answers to these questions (which implies changing the declaration / definition of the constexpr variable that needs to be used), mainly by adding the definition in the compilation unit).

I created a small make_string() template make_string() function 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 it works very well and can be used as

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

However, when printing elements of the static constexpr class, a problem arises, 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 leads to an error during the connection. The reason is because make_string takes all its arguments by reference, including static constexpr max_offset . As a result, a link to foo::max_offset will be required when linking, see Also .

How can I avoid this problem without giving up the idea of make_string() ? (Perhaps you can replace the variational template with a variable macro, but I would consider it to be some kind of regression.) There must be a way to make_string to accept its arguments by value or by reference, depending on the type (so that built-in types can be accepted by value). How?

+5
c ++ c ++ 11 templates argument-passing variadic-templates
Mar 14 '14 at 9:31
source share
2 answers

First, I'm not sure why you need code for make_string . I would just 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&&...) { } }; 

- An auxiliary structure that allows you to evaluate expressions in the correct order (but look, GCC incorrectly evaluates right to left to 4.9 at least).




Now, to your question. As I said in my comment, I feel that your problem is not related to make_string . In Undefined there is a link to a static member of a class , in my question is passing a static constant constexpr by a universal link? , and in all the relevant questions I saw, the suggested answer is that you define a variable somewhere outside the class:

 constexpr int foo::max_offset; 

I am not sure if this is a problem for you. This is a problem for me, because in heavily programmed code this means too much duplication (see the discussion below for my question). Anyway, if this is a problem, I see several other simple solutions for providing a 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; } static constexpr int max_offset() { return some_value; } , then use max_offset() instead of max_offset for

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

  • finally define make_string(Args... args) (this is the easiest, but it doesn’t apply here, since you don’t want to copy all these lines)

I do not discuss the use of make_string when throwing exceptions; this is another problem.

+2
Mar 14 '14 at 22:28
source share

I am not sure if the compiler is correct in making it <strike> the panties in a bunch of jimmies, hatched with a link to constexpr here.

However you can find your way out using boost

  • call_traits<T>::param_type

    Defines the type that represents the "best" way to pass a parameter of type T to a function.

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

+3
Mar 14 '14 at 9:47
source share



All Articles