2016-07-04 47 views
4

我想实现一个函数,它接受可变数量的字符串并转发给打印函数,该函数需要每个字符串的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来接受和处理元组。

回答

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

可能有一些问题,暧昧的函数名,所以你可能需要更改这些辅助函数的名称,但希望这是足以让你走了。

+0

您的解决方案效果很好,谢谢!通过使用这个包装器,我不必修改doPrint,只需添加新的重载。但我认为这三个点应该是左边的一个括号:'doPrint(std :: tuple_cat(...))'而不是'doPrint(std :: tuple_cat()...)',否则top-不会生成级别元组,tuple_cat或多或少是NOP。 –

+0

然而,这需要C++ 14,所以我现在不能在我的代码中使用它。 –

+1

@GeorgiosBitzes你可以编写你自己的'make_index_sequence'和'integer_sequence'。 – SirGuy

5

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

然后它会重复这个每个参数,所以它实际上只会与字符串大小调用doPrint

你应该改变doPrint来代替使用元组,否则你必须使用一些疯狂的模板元编程的东西。

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

贾森·特纳展示了一个简洁的方式在这个视频中使用初始化列表扩展可变参数模板:

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

这适用于克++ -std = C++ 11

+0

有趣的技术,但我不明白它是如何工作的,而不是尝试将参数传递给另一个函数。 – SirGuy

+0

这个想法是,你不需要使用这种技术重定向/专门化。我已经重写它使用转发功能,但你可以直接调用do_print。 – m3mb3rsh1p

+0

如果我没有弄错,do_print在这种情况下收到一个字符串参数包,这是不可取的。 doPrint期望以'char *'和'size'交错的方式调用,它是外部库中的一个函数,我不能改变它。 –