括号内的参数包扩展会产生奇怪的输出

时间:2016-07-04 16:15:18

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

我正在尝试实现一个接受可变数量的字符串并转发到print函数的函数,该函数需要char指针和每个字符串的size,交错。

示例:

std::string a = "123";
std::string b = "1234";
forward(a, b); // should call doPrint(a.c_str(), a.size(), b.c_str(), b.size())

我认为以下内容应该是正确的实现,但即使它编译行为对我来说也是非常令人惊讶的。

template <class ...Args>
void forward(const Args & ... args) {
  doPrint( (args.c_str(), args.size())...);
}

forward(a, b)拨打doPrint(3, 4),而不是doPrint("123", 3, "1234", 4),就像我写了doPrint((args.size())...)一样。编译器完全忽略对c_str()的调用。

我尝试了g++clangicc所有产生相同的输出。 (args.c_str(), args.size())...有什么问题?

的确,std::make_tuple(args.c_str(), args.size())...按预期工作,但我要说我不能更改doPrint来接受和处理元组。

4 个答案:

答案 0 :(得分:7)

逗号运算符是一个表达式,其值是最后一个表达式的值 例如:

    int a = (1, 2, 3, 4, 5, 6);
    assert(a == 6);

您可以尝试使用元组:

    doPrint(std::tuple_cat(std::make_tuple(argc.c_str(), args.size())...));

然后需要更改doPrint以使用元组;如果需要,它可以将元组解包回参数包中,或者直接使用元组。

示例解包元组:

    template <class Tuple, std::size_t ... indices>
    doPrint(Tuple t, std::integer_sequence<size_t, indices...>)
    {
        doPrint(std::get<indices>(t)...);
    }

    template <class Tuple>
    doPrint(Tuple t)
    {
        doPrint(t, std::make_index_sequence<std::tuple_size<Tuple>::value>());
    }

模糊函数名称可能存在一些问题,因此您可能需要更改这些辅助函数的名称,但希望这足以让您继续使用。

答案 1 :(得分:5)

(args.c_str(), args.size())是逗号分隔的表达式,意味着只有最后一部分(args.size())将被传递给函数。

然后它将为每个参数重复此操作,因此它实际上只会使用字符串大小调用doPrint

您应该将doPrint更改为使用元组,否则您必须使用一些疯狂的模板元编程。

答案 2 :(得分:3)

我可能会这样做,以避免将元组暴露给编程接口:

#include <string>
#include <utility>
#include <tuple>

extern void doPrint(...);

namespace detail {
  template<std::size_t...Is, class Tuple>
  void forward(std::index_sequence<Is...>, Tuple&& tuple)
  {
    doPrint(std::get<Is>(tuple)...);
  }
}

template<class...Strings>
void forward(Strings&&... strings)
{
  detail::forward(std::make_index_sequence<sizeof...(Strings) * 2>(),
          std::tuple_cat(std::make_tuple(strings.data(), strings.size())...)
          );
}

int main()
{
  std::string a = "123";
  std::string b = "1234";
  forward(a, b); // should call doPrint(a.c_str(), a.size(), b.c_str(), b.size())
}

答案 3 :(得分:1)

Jason Turner演示了使用此视频中的初始化列表扩展可变参数模板的简明方法:

http://articles.emptycrate.com/2016/05/09/variadic_expansion_wrap_up.html

template< typename ... T >
void do_print(T ... args)
{
    (void)std::initializer_list<int> {
        (std::cout << args.c_str() << ": "
        << args.size() << "\n",     0)...
    };
}

template< typename ... T >
void forward_print(T ... args)
{
    do_print(args...);
}
int main(int argc, const char * argv[])
{
    std::cout << "Hello, World!\n";

    std::string a = "1234";
    std::string b = "567";

    forward_print(a, b);

    return 0;
}

这适用于g ++ -std = c ++ 11