2017-09-01 160 views
20

我使用cppreference閱讀了有關std::vector的扣除指南。C++ 17中的std :: vector演繹指南是什麼?

例子:

#include <vector> 

int main() { 
    std::vector<int> v = {1, 2, 3, 4}; 
    std::vector x{v.begin(), v.end()}; // uses explicit deduction guide 
} 

所以,我有一些關於它的問題:

  • 什麼是C++ 17 std::vector扣除導遊?

  • 爲什麼和什麼時候我們需要矢量扣除?

  • 這裏,是x a std::vector<int>std::vector<std::vector<int>>

+3

'x'可能是'std :: vector :: iterator>'。 –

+0

嘗試添加值到x看到:[演示](http://coliru.stacked-crooked.com/a/640910c60c5735c1)所以'std :: vector :: iterator>'。 – Jarod42

+0

你知道一般的演繹指南嗎?如果沒有,[this](http://en.cppreference.com/w/cpp/language/class_template_argument_deduction#User-defined_deduction_guides)可能會有用。 – HolyBlackCat

回答

17

What are std::vector deduction guides in C++17?

一個用戶定義的扣除指南允許用戶決定如何class template argument deduction推斷論據,從它的構造函數參數的模板類。在這種情況下,似乎std::vector有一個明確的指導,應該使迭代器對的構建更直觀。


Why and when do we need vector deduction?

我們不「需要」,但它是在通用的代碼和代碼是非常明顯的有用的(即代碼,其中明確指定模板參數是不利於讀者)


Is x a vector<int> or a vector<vector<int>> ?

這裏是一個不錯的把戲很快想出解決辦法 - 寫一個模板函數聲明沒有定義,並嘗試調用它。編譯器將打印出傳遞參數的類型。下面是G ++ 8打印出:

template <typename> 
void foo(); 

// ... 

foo(x); 

error: no matching function for call to foo(std::vector<__gnu_cxx::__normal_iterator<int*, std::vector<int> > ...

你可以從錯誤信息看,x被推斷爲std::vector<std::vector<int>::iterator>


Why?

std::vector的演繹指導are available on cppreference.org。標準似乎定義從一個迭代器對顯式扣導向:

enter image description here

以g ++ 8遇到的行爲似乎是正確的,無論作爲(引用Rakete1111

  • overload resolution prefers the constructor with std::initializer_list with the braced initializer list

  • other constructors are considered only after all std::initializer_list constructors have been tried in list-initialization

std:vector<std::vector<int>::iterator>因此在使用列表初始化時是正確的結果。 live example

在構建xstd::vector x(v.begin(), v.end())時,將推導出intlive example

+4

你的最後一點只有一半是真的。即使缺少扣除指南,類型也將是'std:vector :: iterator>',因爲重載解析優先於帶有支撐初始化列表的'std :: initializer_list'的構造函數。 – Rakete1111

+0

@ Rakete1111這不是cppreference上的例子 - 我不知道這個例子是否正確,但在某些情況下,用戶定義的演繹指南優先於自動演繹指南,這可能會使演繹在這裏變得正確。 – Holt

+0

@ Rakete1111根據[最佳可行功能](http://en.cppreference.com/w/cpp/language/overload_resolution#Best_viable_function),「6)或者,如果不是那樣,F1是從用戶定義的扣減指南和F2是不是「,我認爲這意味着具有演繹指南的構造函數是重載分辨率的首選。 – songyuanyao

5

What are std::vector deduction guides in C++17?

Class template argument deduction規定:「爲了實例化一個類模板,每個模板參數必須是已知的,但不是每個模板參數必須指定。」

這就是std:vector本地化,我的意思是std:vector只是一個類。沒什麼特別的。

這裏是裁判的std::vector deducation指南:

template< class InputIt, 
      class Alloc = std::allocator<typename std::iterator_traits<InputIt>::value_type>> 
vector(InputIt, InputIt, Alloc = Alloc()) 
    -> vector<typename std::iterator_traits<InputIt>::value_type, Alloc>; 

如果你不熟悉的語法,請閱讀What are template deduction guides in C++17?

Why and when do we need vector deduction?

您需要導遊當類型從扣參數不是基於這些參數之一的類型。

Is x a vector<int> or a vector<vector<int>> ?

兩者都不!

這是一個:

std::vector<std::vector<int>::iterator> 

強制簡單編譯錯誤(通過分配一個編號以x例如)將推出型):

error: no match for 'operator=' (operand types are 'std::vector<__gnu_cxx::__normal_iterator<int*, std::vector<int> >, std::allocator<__gnu_cxx::__normal_iterator<int*, std::vector<int> > > >' and 'int') 

PS:

Why do we need that initialization, Alloc = Alloc() in that guide?

T帽子的默認參數,它允許傳入一個分配器。違約意味着你不需要兩個嚮導。

+2

爲什麼我們需要在該指南中進行初始化,「Alloc = Alloc()」? 「Alloc」不足以代表該類型本身嗎? –

+0

@DeanSeo這是一個默認參數。 – Barry

+0

@巴里右。但是,我們從扣除指南中需要的是類型,而不是參數?編譯器已經知道它的_default_類型沒有* Alloc()*它看起來像? –

7

Here, Is x a std::vector<int> or a std::vector<std::vector<int>> ?

其他答案在這裏解決您的其他問題,但我想解決這個問題更徹底一點。當我們正在做類模板參數推導時,我們從構造函數synthesize a bunch of function templates,然後從deduction guides更多一些,並執行重載決議,以確定正確的模板參數。

有相當多的構造是std::vector<T,A>,但他們大多不提T這將使T非推斷上下文,因此不會在此重載一個可行的選擇。如果我們預先修剪設置爲只使用那些可能是可行的:通過降低分配器

template <class T> vector<T> __f(size_t, T const&); // #2 
template <class T> vector<T> __f(vector<T> const&); // #5 
template <class T> vector<T> __f(vector<T>&&);   // #6, NB this is an rvalue ref 
template <class T> vector<T> __f(initializer_list<T>); // #8 

然後還該deduction guide,這也是我將簡化:

template <class InputIt> 
vector<typename std::iterator_traits<InputIt>::value_type> __f(InputIt, InputIt); 

這些都是我們的5位候選人,我們像[dcl.init]一樣通過__f({v.begin(), v.end()})致電超載。因爲這是列表初始化,所以我們start with the initializer_list candidates,並且,如果沒有的話,我們是否會進入其他候選者。在這種情況下,有一個initializer_list候選人是可行的(#8),所以我們選擇它甚至沒有考慮任何其他人。該候選者將T推導爲std::vector<int>::iterator,因此我們接着重新啓動重載解析過程,以選擇std::vector<std::vector<int>::iterator>列表的構造函數,並使用兩個迭代器進行初始化。

這可能不是理想的結果 - 我們可能想要一個vector<int>。該解決方案有很簡單:使用() S:

std::vector x(v.begin(), v.end()); // uses explicit deduction guide 

現在,我們沒有做列表初始化所以initializer_list候選人是不是一個可行的候選者。因此,我們通過扣除指南(唯一可行的候選者)推導出vector<int>,並最終調用它的迭代器對構造函數。這有實際使評論正確的副作用。


這是很多地方與{}初始化做一些事情比()初始化完全不同的一個。有人認爲{}是統一的初始化 - 像這樣的例子似乎反對。我的經驗法則:當你特意使用{}時,有意識地需要{}提供的行爲。否則爲()

相關問題