2017-02-28 214 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

相關問題