什么是C++ 17中的std :: vector deduction指南?

msc*_*msc 23 c++ templates vector template-argument-deduction c++17

我阅读了有关std::vector使用cppreference的演绎指南.

例:

#include <vector>

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

所以,我有一些问题:

  • 什么是std::vectorC++ 17 中的演绎指南?

  • 为什么以及何时需要向量推导?

  • 在这里,是x一个std::vector<int>还是一个std::vector<std::vector<int>>

Vit*_*meo 18

什么是std::vectorC++ 17 中的演绎指南?

用户定义的演绎指南允许用户决定类模板参数推导如何从其构造函数参数中推导出模板类的参数.在这种情况下,似乎std::vector有一个明确的指南,应该使迭代器对的构造更直观.


为什么以及何时需要向量推导?

我们并不"需要"它,但它在通用代码和代码中非常有用(即明确指定模板参数的代码对读者无益).


x一个vector<int>还是一个vector<vector<int>>

这是一个很好的技巧来快速解决这个问题 - 编写一个没有定义的模板函数声明并尝试调用它.编译器将打印出传递的参数的类型.这是g ++ 8打印出来的:

template <typename> 
void foo();

// ...

foo(x);
Run Code Online (Sandbox Code Playgroud)

错误:没有匹配的函数用于调用 foo(std::vector<__gnu_cxx::__normal_iterator<int*, std::vector<int> > ...

正如您从错误消息中看到的那样,x推断出来了std::vector<std::vector<int>::iterator>.


为什么?

std::vector可以在cppreference.org上找到扣除指南.标准似乎从迭代器对定义了一个明确的演绎指南:

在此输入图像描述

g ++ 8中遇到的行为似乎是正确的,无论如何(引用Rakete1111)

  • 重载决策更喜欢std::initializer_list带有支撑初始化列表的构造函数

  • 只有std::initializer_list列表初始化中尝试了所有构造函数之后,才会考虑其他构造函数

std:vector<std::vector<int>::iterator>因此,使用列表初始化时是正确的结果. 实例

在构建xstd::vector x(v.begin(), v.end()),int将被推导出来.实例

  • 并且该cppreference示例现已修复. (5认同)
  • 你的最后一点只有一半是真的.即使有缺少的演绎指南,类型也将是`std:vector <std :: vector <int> :: iterator>`因为重载解析更喜欢具有带有支撑初始化列表的`std :: initializer_list`的构造函数. (4认同)
  • 什么@Rakete1111说.`std:vector <std :: vector <int> :: iterator>`是正确的结果. (4认同)
  • @songyuanyao是的我也这么认为,但是不是在{list-initialization]中尝试了所有`std :: initializer_list`构造函数之后才考虑其他构造函数的情况(http://en.cppreference) .COM/W/CPP /语言/ overload_resolution#列出初始化)?所以,迭代器对构造函数甚至不被认为是我认为的. (2认同)

Bar*_*rry 7

在这里,是x一个std::vector<int>还是一个std::vector<std::vector<int>>

这里的其他答案解决了你的其他问题,但我想更彻底地解决这个问题.当我们进行类模板参数推导时,我们从构造函数中合成了一堆函数模板,然后从演绎指南中合成了一些函数模板,并执行重载决策以确定正确的模板参数.

有很多构造函数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
Run Code Online (Sandbox Code Playgroud)

然后还有这个演绎指南,我也将通过删除分配器来简化:

template <class InputIt>
vector<typename std::iterator_traits<InputIt>::value_type> __f(InputIt, InputIt );
Run Code Online (Sandbox Code Playgroud)

那些是我们的5个候选人,我们正在超载,好像是通过[dcl.init]来调用__f({v.begin(), v.end()}).因为这是列表初始化,我们initializer_list候选人开始,并且只有在没有候选人的情况下,我们才会继续其他候选人.在这种情况下,有一个initializer_list可行的候选人(#8),所以我们选择它,甚至没有考虑任何其他人.该候选者推导Tstd::vector<int>::iterator,所以我们然后重新启动重载决策过程以选择一个构造函数,用于std::vector<std::vector<int>::iterator>使用两个迭代器进行list-initialized.

这可能不是理想的结果 - 我们可能想要一个vector<int>.解决方案很简单:使用()s:

std::vector x(v.begin(), v.end()); // uses explicit deduction guide
Run Code Online (Sandbox Code Playgroud)

现在,我们没有进行列表初始化,因此initializer_list候选人不是一个可行的候选者.因此,我们vector<int>通过演绎指南(唯一可行的候选者)推导出来,并最终调用它的迭代器对构造函数.这有利于实际使评论正确.


这是初始化的许多地方之一,与初始化有{}很大不同().有人认为这{}是统一初始化 - 这样的例子似乎反驳了.我的经验法则:{}当你特意使用时,有意识地需要{}提供的行为.()除此以外.


gsa*_*ras 5

什么是std::vectorC++ 17 中的演绎指南?

类模板参数推导指定:"为了实例化类模板,必须知道每个模板参数,但不是必须指定每个模板参数."

这是本地化的std:vector,我的意思是一个std:vector只是一个类.没什么特别的.

这是std::vectorref 的教育指南:

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>;
Run Code Online (Sandbox Code Playgroud)

如果您不熟悉语法,请阅读C++ 17中的什么是模板推理指南?

为什么以及何时需要向量推导?

当从参数中推断出类型不是基于其中一个参数的类型时,您需要指南.

是xa vector<int>还是vector<vector<int>>

都不是!

这是一个:

std::vector<std::vector<int>::iterator>
Run Code Online (Sandbox Code Playgroud)

强制一个简单的编译错误(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')
Run Code Online (Sandbox Code Playgroud)

PS:

为什么我们需要在该指南中进行初始化,Alloc = Alloc()?

这是一个默认参数,允许传入一个分配器.默认意味着您不需要两个指南.

  • 为什么我们需要那个初始化,那个指南中的'Alloc = Alloc()`?是不是"Alloc"足以代表类型本身? (2认同)