Variadic template trouble matching const and non-const std::string

白昼怎懂夜的黑 提交于 2019-12-24 13:04:43

问题


I'm having trouble constructing a variadic template where the expansion functions properly match both a non-const and const std::string. I have a generic matching function which is called instead in some cases.

I've reduced the code to the below example. Things which aren't a string should end up at the generic function. Things which are a string should end up at the second function which additionally outputs "STRING".

#include <iostream>
#include <string>

template<typename X, typename T>
void extend_exception( X & ex, T&& value )
{
    ex << value << std::endl;
}

template<typename X>
void extend_exception( X & ex, std::string && value )
{
    ex << "STRING: " << value << std::endl;
}

template<typename X, typename TF, typename ... TR>
void extend_exception( X & ex, TF&& f, TR&& ... rest )
{
    extend_exception( ex, std::forward<TF>(f) );
    extend_exception( ex, std::forward<TR>(rest)... );
}

int main()
{
    extend_exception( std::cout, std::string( "Happy" ) );
    std::string var( "var" );
    extend_exception( std::cout, var );
    extend_exception( std::cout, var, std::string( "Combo" ),  const_cast<std::string const&>(var));
    return 0;
}

In the version above the two var uses do not get passed to the string function. I have tried various other ways of specifying the parameter but none seem to do the trick: std::string && value, std::string const & value, std::string const && value. I did also try just std::string & value and then the non-const strings match, but not the const ones. This provides me with at least a workaround by having two different functions, but I suspect I shouldn't need to do this.

How do I write a single function here that will be called for const, non-const, and temporary string objects?

I'm using g++ 4.6.3


回答1:


This is pretty tricky, T&& is a so called universal reference but std::string&& is not (no type deduction is involved). See here for a good explanation. Here is a fix:

template<typename X, typename T>
void extend_exception_( X & ex, T&& value, ... )
{
  ex << value << std::endl;
}

template<typename X, typename T,
         typename = typename std::enable_if<
           std::is_same<std::string, typename std::decay<T>::type>::value>::type>
void extend_exception_( X & ex, T && value, int )
{
  ex << "STRING: " << value << std::endl;
}

template <typename T> using identity = T;

template<typename X, typename ... R>
void extend_exception( X & ex, R&& ... rest )
{
  identity<bool[]>{false,(extend_exception_(ex, std::forward<R>(rest), 0), false)...};
}

int main()
{
  extend_exception( std::cout, std::string( "Happy" ) );
  std::string var( "var" );
  extend_exception( std::cout, var );
  extend_exception( std::cout, var, std::string( "Combo" ) );
}



回答2:


This is because your string version only captures strings passed as rvalues. lvalues will get passed to the generic function. There is a huge difference between T&& and string&&, or more general T&& and U&&, if T is a template parameter and U is not (meaning, U is some specified type). See Scott Meyers' notes on universal references

What you need to do in order to make the function work as you want is add yet another overload:

template<typename X>
void extend_exception( X & ex, std::string const& value )
{
  ex << "STRING: " << value << std::endl;
}



回答3:


You can't bind a non-rvalue to an rvalue-reference, so string&& doesn't work, but const std::string& works for var. However, var is not const, so the shortest path to an overload that works is collapsing T&& to std::string& and taking the first one.

You'll need to figure out types that are mutually exclusive or use SFINAE to write two that exclude each other on the right type of arguments.




回答4:


I revisited the problem and came up with a solution which involves using a class marker. I've written an article with the full solution/discussion.



来源:https://stackoverflow.com/questions/14241495/variadic-template-trouble-matching-const-and-non-const-stdstring

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!