2017-02-28 218 views
2

我的示例代码下面的代码片段:C++:模板参数推导通过模板函数作为参数传递给其他模板函数时

的想法是,有一个容器类,这里所谓的Box,那我们可能要通过mapping函数对当前内容创建此容器的新版本。

#include <iostream> 
#include <tuple> 

template <typename TContent> 
class Box 
{ 
    TContent d_content; 

    public: 

    Box(TContent content) 
    : 
    d_content(content) 
    {} 

    TContent content() const 
    { 
    return d_content; 
    } 

    template <typename Function> 
    auto transform(Function fun) -> decltype(Box{fun(d_content)}) 
    { 
    return Box{fun(d_content)}; 
    }; 

}; 

template <typename TElem> 
std::tuple<TElem, TElem> toTuple(TElem thing) 
{ 
    std::cout << "Transforming " << thing << "to tuple.\n"; 
    return std::make_tuple(thing, thing); 
} 

int main() { 
    std::cout << "Hello World!\n"; 

    Box<int> mybox{10}; 
    Box<int> box2 = mybox.transform([](int content){ return content * 2;}); 
    std::cout << "Transformed box: " << box2.content() << '\n'; 
    Box<std::tuple<int, int>> box3 = mybox.transform(&toTuple); // <- Template argument deduction/substitution fails here! 
    std::cout << "Transformed box: " << std::get<0>(box3.content()) << '\n'; 
} 

Try it out here

在管线42,在其中创建BOX3,模板参数推导/置换失败:

main.cpp: In function 'int main()': 
main.cpp:42:60: error: no matching function for call to 'Box<int>::transform(<unresolved overloaded function type>)' 
    Box<std::tuple<int, int>> box3 = mybox.transform(&toTuple); 
                  ^
main.cpp:22:8: note: candidate: template<class Function> decltype (Box<TContent>{fun(((Box<TContent>*)this)->Box<TContent>::d_content)}) Box<TContent>::transform(Function) [with Function = Function; TContent = int] 
    auto transform(Function fun) -> decltype(Box{fun(d_content)}) 
     ^~~~~~~~~ 
main.cpp:22:8: note: template argument deduction/substitution failed: 
main.cpp:42:60: note: couldn't deduce template parameter 'Function' 
    Box<std::tuple<int, int>> box3 = mybox.transform(&toTuple); 
                  ^

exit status 1 

这似乎是试图通过一个模板函数时的情况下(函数模板?)到一个本身需要模板参数参数的函数。

迄今为止发现的避免这种情况的唯一方法是将所有模板函数包装在lambdas或其他非模板函数中。这当然不是最理想的,因为它引入了大量的样板。

为什么模板参数推导失败在这种情况下,是有没有办法改变Box类的代码(和/或它的transform成员函数),以确保模板参数推导确实工作?

(给出的代码是C++ 11作为repl.it尚不支持C++ 14。在C++ 14的主要区别将是的transform尾随返回类型可以被省略。该错误,但是,仍然是相同的,我很高兴与解决方案(只)工作在C++ 14为好)

回答

2

在您的例子在这里:。

template <typename Function> 
auto transform(Function fun) -> decltype(Box{fun(d_content)}) 

Box是一类模板,不是班级。在课堂上,Box注入类名称,它总是专指Box<TContent>。因此,如果您需要更改类型(因为transform可能非常需要),但这不起作用。你需要指定Box您可以根据什么Function是:

template <class Function, 
    class U = std::result_of_t<Function&(TContent)>> 
Box<U> transform(Function fun) 
{ 
    return Box<U>{fun(d_content)}; // or even better, Box<U>{std::invoke(fun, d_content)} 
}; 

的第二个问题是,当你把它叫做:

Box<std::tuple<int, int>> box3 = mybox.transform(&toTuple); 

toTuple是一个函数模板,而不是一个函数。你不能将它传递给另一个函数模板,因为它没有类型,所以不能推导出它。像以前一样,你需要指定toTuple你想要的:

Box<std::tuple<int, int>> box3 = mybox.transform(toTuple<int>); 

或包裹了整个事情的拉姆达(这是一个简化的实施不关心拷贝,引用或SFINAE):

Box<std::tuple<int, int>> box3 = mybox.transform([](auto x) { return toTuple(x); }); 
+0

谢谢。出于某种原因,C++ 11和C++ 14愉快地编译格式错误的decltype。无论如何,将它改为“Box ”会使它正确,对吗? – Qqwy

+0

至于''toTuple'是一个函数模板,不是函数,你不能将它传递给另一个函数模板,因为它没有类型,所以不能推导出来。“ - >是否有将函数模板传递给另一个函数模板的情况,可以推导出它? – Qqwy

+1

@Qqwy是的,你可以。不,没有。 – Barry

相关问题