2016-08-27 44 views
2

因此,當我一直在使用C++學習模板時,我決定想出一些不尋常的情況,看看我能否讓它們工作。 (不,這是不實際的 - 只是爲了玩這種語言!)我創建了一個模板類,其類型值爲T,帶有可變參數函數模板,返回std::pair,T和參數中的一個值的最大值包。但是,我無法編譯它。這是我寫的......如何在非可變模板類中形成可變模板函數?

header.h

#ifndef HEADER_H 
#define HEADER_H 

#include <utility> 
#include <algorithm> 
#include <array> 

template <typename T> 
class MyClass 
{ 

    T m_heldType; 

public: 

    MyClass(T t) : m_heldType(t) {} 

    template <typename... T2> 
    std::pair<T, T2> pairingFunc(T2&&... types) 
    { 
    std::array<T2, sizeof...(types)> types_arr = { types... }; 

    return std::make_pair(m_heldType, *std::max_element(types_arr.begin(), types_arr.end())); 
    } 

}; 

#endif 

source.cpp

#include <iostream> 
#include "Header.h" 

int main() 
{ 
    MyClass<int> mc(512); 

    auto mypair = mc.pairingFunc(1.61f, 3.14f, 6.66f); 

    std::cout << mypair.first << ' ' << mypair.second; 
    std::cin.ignore(); 
} 

這些都是我產生了錯誤:

Error C3520 'T2': parameter pack must be expanded in this context ...\header.h 24 
Error C2672 'MyClass<int>::pairingFunc': no matching overloaded function found ...\source.cpp 8 
Error C2893 Failed to specialize function template 'std::pair<T,T2> MyClass<T>::pairingFunc(T2 &&...)' ...\source.cpp 8 
Error C3536 'mypair': cannot be used before it is initialized ...\source.cpp 10 
Error C2228 left of '.first' must have class/struct/union ...\source.cpp 10 
Error C2228 left of '.second' must have class/struct/union ...\source.cpp 10 

所以這裏就是我想:

  • 顯然,編譯器無法確定的mypair類型(初始化失敗)。但爲什麼?它知道MyClass中的T的類型和pairingFunc()中的T2的類型。明確聲明std::pair<int, float>修復了此錯誤,但留下了其他錯誤(潛在問題的症狀)。
  • 「無法專門化功能模板」...我猜這意味着它不能根據給定的類型返回類型?如果是這樣,爲什麼不呢?
  • 「參數包必須在此上下文中擴展」 - 我不確定這個。通過將包放入數組中不會發生這種情況嗎?

此外,我想通過強制執行類似(T2&& head, T2&&... tail)至少有一個參數的規定,但我認爲,讓這兩個到一個數組或向量可能是討厭的,我不知道該如何處理只是單一的可變參數。所以這只是我猜想的一個'獎金'。

回答

2

的問題是在這裏:

std::pair<T, T2> pairingFunc(T2&&... types) 
      ^^ 

這裏

std::array<T2, sizeof...(types)> types_arr = { types... }; 
      ^^ 

T2是一個參數包,而不是一個類型。它可以存儲多種類型,即1.4f, "hi", 1, 0.5。所以你不能將它作爲單一類型使用,這是不可能的。您需要引入另一個參數,並使用它作爲一個類型:

template <typename T1, typename... T2> 
std::pair<T, T1> pairingFunc(T1&& arg, T2&&... types) 
{ 
    std::array<T1, sizeof...(types) + 1> types_arr = { arg, types... }; 

    return std::make_pair(m_heldType, *std::max_element(types_arr.begin(), types_arr.end())); 
} 

這也具有的優點是,如果你調用

mc.pairingFunc(4.5f, "hello"); 

它不會編譯。不帶參數地調用它也是不可能的(儘管如此,這是沒有意義的)。


優選的溶液(感謝@DanielSchepler)可能是使用std::common_type,因爲它可能是可能的是,第二個參數是無法轉換爲T,但T可轉化爲第二個參數:

template <typename T1, typename... T2> 
std::pair<T, std::common_type_t<T1, T2...>> pairingFunc(T1&& arg, T2&&... types) 
{ 
    std::array<std::common_type_t<T1, T2...>, sizeof...(types) + 1> types_arr = { arg, types... }; 

    return std::make_pair(m_heldType, *std::max_element(types_arr.begin(), types_arr.end())); 
} 
+2

或者,在數組類型和返回類型的第二個成員中使用'std :: common_type_t '可能是合適的。 –

+0

@DanielSchepler好主意,不知道'std :: common_type',謝謝:) – Rakete1111

+0

@ Rakete1111謝謝你的回答!我認爲引用'T2'只是意味着'一個T2類型的數組'而不是整個參數包。這意味着我越來越接近(頭,尾......)的思維。此外,它還增加了'必須至少有一個arg'限制和單一打字。再一次,謝謝你,這是一個明確而重要的答案。 –

2

的根本問題是,參數包

<typename ...T2> 

不代表單一類型。

template <typename... T2> 
void foo(T2 && ...args) 

該參數組可以接受的類型的組合,即

foo(4, "bar", Baz()); 

你需要弄清楚,pairingFunc()接收參數的類型。建議std::common_type_t是一個不錯的選擇:

template <typename T> 
class MyClass 
{ 

    T m_heldType; 

public: 

    MyClass(T t) : m_heldType(t) {} 

    template <typename ...T2> 
    auto pairingFunc(T2 &&...values) 
    { 
     std::array<std::common_type_t<T2...>, sizeof...(T2)> types_arr = { values... }; 

     return std::make_pair(m_heldType, *std::max_element(types_arr.begin(), types_arr.end())); 
    } 

}; 
+0

謝謝,這個答案也很好,但我仍然接受另一個使用至少需要一個arg的方法簽名,而不使用'std :: common_type'。但現在我知道了,我可以將它添加到我的武庫中,可以這麼說。 –

+1

不使用'std :: common_type',值的類型被強制匹配第一個參數。即pairingFunc(0,1.2f,3.4f)會產生一個編譯器警告,因爲內部的'std :: array'將包含int,而不是float。 –

+0

因此,'std :: common type'也考慮以下類型的隱式轉換?我想這可能或多或少是可取的,取決於上下文(使用文字我寧願明確地說'f'或'l'等等),但是在某個地方仍然有用。 –