1

我在构建可变参数模板时遇到问题,其中扩展函数正确匹配非 const 和 const std::string。我有一个通用匹配函数,在某些情况下会调用它。

我已将代码简化为以下示例。不是字符串的东西应该以通用函数结尾。作为字符串的东西应该在另外输出“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;
}

在上面的版本中,这两种var用法不会传递给字符串函数。我尝试了各种其他指定参数的方法,但似乎都没有成功:std::string && value, std::string const & value, std::string const && value. 我也确实尝试std::string & value过,然后非常量字符串匹配,但不是 const 字符串。这通过具有两个不同的功能为我提供了至少一种解决方法,但我怀疑我不需要这样做。

我如何在这里编写一个函数来调用 const、非常量和临时字符串对象?

我正在使用 g++ 4.6.3

4

4 回答 4

4

这很棘手,T&&是所谓的通用引用,但std::string&&不是(不涉及类型推导)。请参阅此处以获得很好的解释。这是一个修复:

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" ) );
}
于 2013-01-09T16:27:09.540 回答
2

您不能将非右值绑定到右值引用,因此string&&不起作用,但const std::string&适用于var. 但是,var它不是 const,因此有效的重载的最短路径是折叠T&&std::string&采用第一个。

您需要找出相互排斥的类型,或者使用 SFINAE 在正确的参数类型上编写两个相互排斥的类型。

于 2013-01-09T16:27:10.173 回答
2

这是因为您的字符串版本仅捕获作为右值传递的字符串。左值将被传递给通用函数。T&& 和 string&&,或者更一般的 T&& 和 U&& 之间存在巨大差异,如果 T 是模板参数而 U 不是(意思是,U 是某个指定的类型)。参见 Scott Meyers 关于通用参考的注释

为了使函数按您的意愿工作,您需要做的是添加另一个重载:

template<typename X>
void extend_exception( X & ex, std::string const& value )
{
  ex << "STRING: " << value << std::endl;
}
于 2013-01-09T16:32:04.887 回答
0

我重新审视了这个问题并提出了一个涉及使用类标记的解决方案。我写了一篇带有完整解决方案/讨论的文章。

于 2013-06-03T07:22:53.017 回答