2017-04-03 39 views
11

以下代碼C++與雙打

#include <initializer_list> 
#include <vector> 

template<int ...> 
const std::vector<int>*make_from_ints(int args...) 
{ return new std::vector<int>(std::initializer_list<int>{args}); } 

被編譯可變參數模板(具有6.3 GCC,在Debian/SID/x86-64的)正確地,我期望它對於像

auto vec = make_from_ints(1,2,3); 
呼叫

的指針返回到含有1,2的整數的一些載體中,3.

然而,如果我通過double替換int,即如果我添加以下(在同一basiletemplates.cc文件...)代碼:

template<double ...> 
const std::vector<double>*make_from_doubles(double args...) 
{ return new std::vector<double>(std::initializer_list<double>{args}); } 

我得到一個編譯錯誤:

basiletemplates.cc:8:17: error: ‘double’ is not a valid type 
       for a template non-type parameter 
template<double ...> 
       ^~~ 

,我不明白爲什麼。畢竟intdouble都是標量數字POD類型(在C++ 11標準中預定義)。

如何獲得一個模板可變參數函數能夠代碼:

auto dvec = make_from_doubles(-1.0, 2.0, 4.0); 

,並得到一個指向包含-1.0,2.0,4.0雙打的一些載體?

順便說一句,編譯爲C++ 14(與g++ -Wall -std=c++14 -c basiletemplates.cc),並使用clang++(版本3.8.1),而不是g++不要更改任何東西。

+9

'整形變量...'被解析爲'整形變量,... ',你的模板的定義是錯誤的 –

回答

23
template<int ...> 
const std::vector<int>*make_from_ints(int args...) 
{ return new std::vector<int>(std::initializer_list<int>{args}); } 

上面的代碼段具有的問題的多種:

  • 返回的const std::vector<int>*代替std::vector<int>和不必要的使用動態分配。

    • 即使您想使用動態分配,您應該使用std::make_unique而不是new
  • 您定義make_from_ints爲模板函數,它的int模板參數任何金額,但你不給那些int是個名字 - 你永遠不能使用它們!

  • 您的簽名實際上被解析爲make_from_ints(int args, ...) - 這是與可變參數模板無關的C va_args簽名。

    • 參數包的正確語法是type... name

如果你想接受任何數量的與模板參數推導很好地工作的特定類型的參數,最簡單的方法是使用接受任意數量的常規可變參數模板類型和static_assert的類型(或使用std::enable_if SFINAE友好)。這裏有一個例子:

template <typename... Ts> 
auto make_from_ints(Ts... xs) 
{ 
    static_assert((std::is_same<Ts, int>::value && ...)); 
    return std::vector<int>{xs...}; 
} 

template <typename... Ts> 
auto make_from_doubles(Ts... xs) 
{ 
    static_assert((std::is_same<Ts, double>::value && ...)); 
    return std::vector<double>{xs...}; 
} 

用法:

for(auto x : make_from_ints(1,2,3,4)) std::cout << x << " "; 
std::cout << "\n"; 
for(auto x : make_from_doubles(1.0,1.5,2.0,2.5)) std::cout << x << " "; 

1 2 3 4

1 1.5 2 2.5

live example on wandbox


請注意,我使用的是C++17 fold expression,檢查所有Ts...是一種特殊類型的在這裏:

static_assert((std::is_same<Ts, int>::value && ...)); 

如果您沒有訪問C++ 17的功能,這可以很容易的東西,如更換:

template <typename... Ts> 
constexpr auto all_true(Ts... xs) 
{ 
    for(auto x : std::initializer_list<bool>{xs...}) 
     if(!x) return false; 

    return true; 
} 

// ... 

static_assert(all_true(std::is_same<Ts, int>{}...)); 
+0

'&& ...'真的是字面意思嗎?這是什麼意思? –

+1

@BasileStarynkevitch:對不解釋的道歉 - 這是一個C++ 17倍表達式。將改善我的答案。 –

+2

除了fold表達式之外,你可以使用'std :: conjunction',雖然'std :: conjunction'是一個C++ 17特性。我發現它比fold表達更容易閱讀:'std :: conjunction ...> value' – Justin