两个集合类型不应该相同吗?
#include <array>
#include <set>
#include <iostream>
template <class Iter>
std::set(Iter, Iter) -> std::set<typename std::iterator_traits<Iter>::value_type>;
int main() {
std::array a {1,2,3,4};
std::set<int> si {a.begin(), a.end()};
std::set s {a.begin(), a.end()};
for(auto& i: si) { std::cout << i << "\n"; }
for(auto& i: s ) { std::cout << i << "\n"; }
}
相反它产生:
1
2
3
4
0x7ffdf5bc9050
0x7ffdf5bc9060
甚至尝试使用其他分配器:-(
答案 0 :(得分:1)
这里有两点困惑。
首先,推论指南必须在他们所指导的类模板的范围内,因此永远不会考虑该指南:
template <class Iter>
std::set(Iter, Iter) -> std::set<typename std::iterator_traits<Iter>::value_type>;
它必须看起来像这样:
namespace std {
template <class Iter>
set(Iter, Iter) -> set<typename iterator_traits<Iter>::value_type>;
}
但是您不允许将内容添加到namespace std
中,因此请勿这样做。此外,此set
的推导指南already exists:
template<class InputIt,
class Comp = std::less<typename std::iterator_traits<InputIt>::value_type>,
class Alloc = std::allocator<typename std::iterator_traits<InputIt>::value_type>>
set(InputIt, InputIt, Comp = Comp(), Alloc = Alloc())
-> set<typename std::iterator_traits<InputIt>::value_type, Comp, Alloc>;
因此,没有任何理由要添加您的版本。
不使用推导指南的原因 ,或者如果将其放在正确的命名空间中也不会使用,则通常是列表初始化的主要警告:
如果有匹配的initializer_list<T>
构造函数,则强烈推荐。
特定的语言规则是,我们首先针对那些构造函数进行重载解析,然后再进行其余的工作。我们列表中的其他推导指南之一是:
template<class Key, class Comp = std::less<Key>, class Alloc = std::allocator<Key>>
set(std::initializer_list<Key>, Comp = Comp(), Alloc = Alloc())
-> set<Key, Comp, Alloc>;
请注意:
std::set s{a.begin(), a.end()};
的构造类型与完全相同:
std::set u{1, 2};
仅仅因为我们相同类型的两个元素是迭代器,并不意味着它们与相同类型的其他两个元素有不同的对待。这样我们得到了一组迭代器。
如果要将其他推导指南与其他构造函数一起使用,则必须 使用括号:
std::set s(a.begin(), a.end());
并删除您的扣除指南!
答案 1 :(得分:0)
std::set
提供了一个构造函数,该构造函数采用std::initializer_list
,并且能够对T
进行std::set<T>
的类型推导。在这种情况下,使用语法std::set x{a.begin(), a.end() };
将使用初始化程序列表构造函数并从中推导T。没有用户定义的扣除指南!
如果使用std::set x(a.begin(), a.end());
创建对象,则将不使用初始化列表构造器。现在,因为没有其他构造函数匹配,所以可以进行推导!
示例在代码中包含初始化器列表构造函数的情况下的效果:
template < typename U >
struct X
{
// Directly makes the type deducable, because U can be deduced from the parameter of the constructor
X( std::initializer_list<U> )
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
template < typename ... T>
X( T... )
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
struct ONE {};
template < typename Iter >
X( const Iter&, const Iter& ) -> X<ONE>;
template < typename U >
struct Y
{
// type U can NOT deduced from the parameter!
template < typename T>
Y( std::initializer_list<T> )
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
template < typename ... T>
Y( T... )
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
struct TWO {};
template < typename Iter >
Y( const Iter&, const Iter& ) -> Y<TWO>;
int main()
{
std::array a {1,2,3,4};
X x1{a.begin(), a.end()};
X x2(a.begin(), a.end());
std::cout << "###" << std::endl;
Y y1{a.begin(), a.end()};
Y y2(a.begin(), a.end());
}
我对初始化器列表构造函数的优先级高于推导指南,这对我来说也是新的,并在此处回答: Why does using uniform initializer syntax result in different behavior to the "old" style ()?
对我来说重要的新事物是:
[...]重载解析分两个阶段选择构造函数:
最初,候选函数是类
T
和参数列表的 initializer-list构造函数([dcl.init.list]) 由初始化程序列表作为单个参数组成。[...]
有关详细信息,请参见链接的问题!