2017-06-29 115 views
1

我的函數使用一組給定的輸入參數(variadic)調用一個Python函數,並返回一個包含該函數輸出的變量(可變參數,因爲輸出因函數調用而變化)。C++ variadic模板返回類型

我正在使用C++ 11通過MinGW-w64編譯器的g ++端口編譯Windows 10機器。我宣佈這個模板可變參數函數(並調用它),如下所示:

// Declaration (in PyInterface.h) 
template <typename... Inputs, typename... Outputs> 
const std::tuple<Outputs...>& callFunction(const std::string &modulePath, const std::string &funcName, Inputs&&... args) { 
    // Calls a Python function & packs output variables into a std::tuple... 
} 

// Sample call (in main.cpp) 
const std::tuple<std::string, std::string> output = pi.callFunction(myModulePath, myFuncName, inputArg1, inputArg2, inputArg3); 

然而,這個錯誤被拋出(縮短了與...的可讀性):

conversion from 'const std::tuple<>' to non-scalar type 'const std::tuple<std::__cxx11::basic_string<...>, std::__cxx11::basic_string<...> >' requested

至於我'知道using two variadic templates is legal。另外,在我看來,返回類型是由變量明確設置的(const std::tuple<std::string, std::string> output)我試圖將函數的結果分配給,所以編譯器應該知道所需的返回類型爲 。

最初,我認爲錯誤只是表明我沒有適當地使用可變參數模板。我嘗試使用嵌套模板參數(圖解說明here)指定返回類型(即const T<Outputs...>& callFunction)。但是,這隻能成功生成一條錯誤消息,指示模板扣除T失敗。

這個錯誤表明我的直覺是錯誤的,並且在我原來的函數中,編譯器沒有從output的類型推導出所需的返回類型。

爲什麼我的直覺錯了?如何正確使用可變參數模板來指定此函數的返回類型?

+1

你從不指定'Outputs'的類型,所以沒有。 – NathanOliver

+0

@NathanOliver您能否詳細說明我如何爲'Outputs'指定類型?我已經嘗試將我的調用修改爲'pi.callFunction ',但我無法弄清楚錯誤消息的含義:'error:can not bind'const string {aka const std :: __ cxx11 :: basic_string }'左值to'std :: __ cxx11 :: basic_string &&' –

+0

您明確指定'string'爲輸入類型。你得到一個右值引用,並與函數參數聲明'Inputs &&'結合。你正在傳遞一個'const string'的左值,在使用它之前至少需要刪除'const'。除了我給出的輸入類型隱式提取的答案之外,您可以顯式指定'const string&'爲Input類型,當與Inputs &&組合時,它仍然是'const string&'。 –

回答

1

如果您想根據函數範圍內返回的對象的類型來推導返回類型,如果您使用的是C++ 14,那麼您可以使用auto作爲返回類型。它像一個魅力

template <typename... Inputs> 
const auto& callFunction(
    const std::string &modulePath, 
    const std::string &funcName, 
    Inputs&&... args) { 

    return object; 
} 

一如既往要提防,你是從這個函數返回一個const引用。

如果您正在使用C++ 11,那麼你可以使用後返回類型,例如

template <typename T> 
auto identity_return(T& obj) -> decltype(obj) { 
    return obj; 
} 

其中decltype(obj)是要返回的東西類型。在這種情況下,再次提防,這又是一個參考。

通常,儘管嘗試並返回函數中的值,但如果您不確定返回的結果是否超出返回值,則返回引用可能會導致懸掛引用。


另一種解決方案是隻手動指定Outputs...類型列表,讓編譯器推斷類型Inputs...

template <typename... Outputs, typename... Inputs> 
const std::tuple<Outputs...>& callFunction(
    const std::string &modulePath, 
    const std::string &funcName, 
    Inputs&&... args) { ... } 

auto tup = callFunction<std::string, std::string>(modulePath, funcName, args); 
+0

您正在返回對已銷燬的本地對象的引用。 – NathanOliver

+0

@NathanOliver這就是爲什麼我最後提到「提防」的原因! – Curious

+0

我猜想我想要溝通的是爲什麼在你的例子中使用錯誤的代碼時,你可以「正確的方式」做到這一點,並向OP解釋你爲什麼這樣做。 – NathanOliver

0

照例與C++編譯器的錯誤消息,第一個是你需要的只有一個看看。我猜(因爲你沒有提供代碼),你正在創建一個tuple值,然後返回一個const引用。在該函數結束時,tuple被破壞,因此參考文獻應引用什麼內容?您應該只返回tuple<Outputs...>

我認爲你可以通過交換InputOutput類型包來實現你想要的。此代碼還說明了使用show函數對Inputs型包進行迭代。您可以執行類似的操作,以從Python元組對象和Outputs類型包構建tuple<Outputs...>值。

#include <iostream> 
#include <tuple> 
#include <utility> 

using namespace std; 

template< typename Output > struct convert_t; 
template<> struct convert_t<string> 
{ 
    string operator()(const char *text) const 
    { 
     return text; 
    } 

    string operator()(double val) const 
    { 
     return to_string(val); 
    } 
}; 

inline void show() 
{} 

template< typename First, typename... Others > 
inline void show(First &&first, Others&&... others) 
{ 
    cout << first << "\n"; 
    show(forward<Others>(others)...); 
} 

template< typename... Outputs, typename... Inputs > 
inline tuple<Outputs...> funkyfunc(Inputs&&... inputs) 
{ 
    cout << "Inputs...\n"; 
    show(forward<Inputs>(inputs)...); 
    return tuple<Outputs...>(convert_t<Outputs>()(inputs)...); 
} 

int main() 
{ 
    auto t = funkyfunc< string, string >("text", 123.0); 
    cout << "Outputs...\n"; 
    cout << get<0>(t) << "\n"; 
    cout << get<1>(t) << "\n"; 

    return 0; 
}