C++ 类模板不起作用的嵌套模板参数推断
在中,我编写了一个小包装器类,它提供对某个范围的反向迭代器访问,依赖于类模板(,)的c++1z语言功能模板参数推断 (g++7.0 SVN和clang 5.0 SVN的输出相同) 罪魁祸首似乎是类模板的模板参数推断,因为通常的包装函数允许正确的嵌套C++ 类模板不起作用的嵌套模板参数推断,c++,templates,c++17,class-template,template-argument-deduction,C++,Templates,C++17,Class Template,Template Argument Deduction,在中,我编写了一个小包装器类,它提供对某个范围的反向迭代器访问,依赖于类模板(,)的c++1z语言功能模板参数推断 (g++7.0 SVN和clang 5.0 SVN的输出相同) 罪魁祸首似乎是类模板的模板参数推断,因为通常的包装函数允许正确的嵌套 template<class Rng> auto MakeReverse(Rng const& rng) { return Reverse<Rng>(rng); } // prints 1,2,3 for (auto
template<class Rng>
auto MakeReverse(Rng const& rng) { return Reverse<Rng>(rng); }
// prints 1,2,3
for (auto const& elem : MakeReverse(MakeReverse(my_stack))) {
std::cout << elem << ',';
}
模板
自动生成反向(Rng const&Rng){返回反向(Rng);}
//打印1,2,3
for(auto const&elem:MakeReverse(MakeReverse(my_stack))){
std::cout这可以在以下内容中解释:
形成一组功能和功能模板,包括:
- 对于由
模板名称,具有以下属性的函数模板:
- 模板参数是类模板的模板参数,后跟模板参数(包括默认值)
构造函数的模板参数(如果有)
- 函数参数的类型是构造函数的类型
- 返回类型是由模板名称和对应于模板的模板参数指定的类模板专门化
从类模板中获取的参数
我的理解是编译器发明了以下两个函数(两个-包括为此类隐式生成的复制构造函数):
包含一个复制构造函数来构造外部反向实例。正确地解释了发生的情况-移动构造函数比构造函数模板更匹配
但是(通常是h/t)有一个比编写工厂更好的修复方法:您可以添加一个明确的演绎指南来处理包装:
template <class R>
Reverse(Reverse<R> ) -> Reverse<Reverse<R>>;
以前,#3
被认为更专业。现在,#4
被认为是扣减指南。因此,我们仍然可以写:
for (auto const& elem : Reverse(Reverse(my_stack))) {
std::cout << elem << ',';
}
for(自动常量和元素:反向(反向(我的堆栈))){
std::cout@cppleerner我不这么认为,my_stack
是一个命名变量,引用存储在Reverse
对象中,所以这里挂起的是什么?但是Reverse(my_stack)
不是一个命名变量,您将对它的引用存储在Reverse(my_stack))中
@TemplateRex它没有直接绑定到那个引用,所以它在第二个代码段中是UB,在第一个代码段中可能是ok,在第一个代码段中使用了move构造,但总的来说,这不是行为的原因外部反向的构造函数是一个复制构造函数,因此使用了相同的模板参数。@PiotrSkotnicki好的,那是有道理,但这仍然令人惊讶。我猜范围包装器仍然需要帮助功能。我可能完全错了,请纠正我,我也很好奇。我认为你是对的,+1。真正的问题是-这是该功能的预期行为,还是否?当然令人惊讶。基本正确,只是它是移动构造函数。@Barry“惊喜”怎么说?@Barry你必须选择一种方式或另一种方式,在我看来,当前的选择比另一种方式带来的惊喜要少。而且,你不需要工厂,一个明确的指南就可以了。@Barry模板反向(R)->Reverse;
应该这样做。在当前的措辞下,您希望R
,而不是R const&
,否则#3获胜,因为(将右值ref绑定到右值).Clang似乎是。这会迫使所有副本再次实际反转容器吗?@DavidStone我不明白这个问题。@Barry谢谢,很高兴掌握这些新的演绎指南。不过我想知道,这是否是新的SFINAE,你真的必须非常仔细地考虑所有的重载,并且你必须给出一个答案lue构造函数是一个演绎指南,用于处理右值匹配。如果能给出一个const&
演绎指南,它会更直观(比如你的意思是原则)。@namark那是因为它总是错的!!哎呀。现在它是对的。复制演绎候选比我以前的指南更专业(在优先顺序列表中,在扣除指南之前有更专业的内容)
template <typename Rng>
Reverse<Rng> foo(const Rng& r); // #1
template <typename Rng>
Reverse<Rng> foo(const Reverse<Rng>& r); // #2
foo(Reverse<std::vector<int>>(my_stack));
Reverse(Reverse(my_stack))
template <class R>
Reverse(Reverse<R> ) -> Reverse<Reverse<R>>;
template <typename Rng>
Reverse<Rng> foo(const Rng& r); // #1
template <typename Rng>
Reverse<Rng> foo(const Reverse<Rng>& r); // #2
template <typename Rng>
Reverse<Rng> foo(Reverse<Rng>&& r); // #3
template <typename Rng>
Reverse<Reverse<Rng>> foo(Reverse<Rng> r); // #4 - same-ish as #2/3, but deduction guide
for (auto const& elem : Reverse(Reverse(my_stack))) {
std::cout << elem << ',';
}