2016-07-04 48 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'交錯的方式調用,它是外部庫中的一個函數,我不能改變它。 –