2017-06-29 117 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; 
}