class 模板的模板参数推导陷阱
Gotchas with template argument deduction for class templates
我正在阅读有关 class 模板的模板参数推导的论文 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0091r3.html。此功能在 C++17 标准中,有些事情让我感到困惑。
template <typename T>
class Something {
public:
// delete the copy and move constructors for simplicity
Something(const Something&) = delete;
Something(Something&&) = delete;
explicit Something(T&&) { ... }
explicit Something(const T&) { ... }
template <typename U, typename EnableIfNotT<U, T>* = nullptr>
Something(U&&) { ... }
};
鉴于上述代码,如果有人试图像这样实例化上述模板的实例
auto something = Something{std::shared_ptr<int>{}};
右值引用重载总是会被调用吗?由于考虑扣除的过载集为
template <typename T>
Something<T> F(T&&) { ... }
template <typename T>
Something<T> F(const T&) { ... }
template <typename T, typename U, typename EnableIfNotT<U, T>*>
Something<T> F(U&&) { ... }
- 第二个重载永远不会优于第一个重载(因为它现在是转发引用重载,而不是右值引用重载),那么这里应该发生什么?
- 如果不明确指定
T
参数,似乎永远无法调用最后一个,这是预期的行为吗?
- 在为 class 模板使用模板参数推导时,是否还有任何其他陷阱或风格指南需要牢记?
- class 定义之后还需要用户定义的推导指南吗?例如,您能否在 class 定义本身的 class 构造函数的声明中包含尾随 return 类型? (不像这里的迭代器构造函数http://en.cppreference.com/w/cpp/language/class_template_deduction)
- The second overload will never be preferred over the first one (since that is now a forwarding reference overload, instead of being an rvalue reference overload), so what is supposed to happen here?
不,它不是转发参考。这是一个关键的区别。来自 [temp.deduct.call]:
A forwarding reference is an rvalue reference to a cv-unqualified template parameter that does not represent a template parameter of a class template (during class template argument deduction ([over.match.class.deduct])).
您的候选人是:
template <typename T>
Something<T> F(T&&); // this ONLY matches non-const rvalues
template <typename T>
Something<T> F(const T&); // this matches everything
template <typename T, typename U, typename EnableIfNotT<U, T>*>
Something<T> F(U&&); // this matches nothing
当你写:
auto something = Something{std::shared_ptr<int>{}};
T&&
构造函数是首选,T=std::shared_ptr<int>
,所以你最终得到 Something<std::shared_ptr<int>>
作为你的 class 模板专业化。如果改写:
std::shared_ptr<int> p;
auto something = Something{p};
那么 T const&
构造函数是首选(实际上它是唯一可行的候选者)。虽然我们最终在同一个地方:Something<std::shared_ptr<int>>
.
- And it seems like the last one can never be called without explicitly specifying the T parameter, is this the intended behavior?
正确,T
是非推导上下文。这是有道理的——此构造函数的存在是为了进行转换,但您需要指定要将 转换为 的内容才能进行转换。为你准备这个 "just work" 是没有意义的。
- Further are user defined deduction guides required to be after the class definition?
是的。按照规则,这就是他们要去的地方。在构造函数中添加尾随 return 类型是没有意义的 - 构造函数没有 "return" 任何东西。
我正在阅读有关 class 模板的模板参数推导的论文 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0091r3.html。此功能在 C++17 标准中,有些事情让我感到困惑。
template <typename T>
class Something {
public:
// delete the copy and move constructors for simplicity
Something(const Something&) = delete;
Something(Something&&) = delete;
explicit Something(T&&) { ... }
explicit Something(const T&) { ... }
template <typename U, typename EnableIfNotT<U, T>* = nullptr>
Something(U&&) { ... }
};
鉴于上述代码,如果有人试图像这样实例化上述模板的实例
auto something = Something{std::shared_ptr<int>{}};
右值引用重载总是会被调用吗?由于考虑扣除的过载集为
template <typename T>
Something<T> F(T&&) { ... }
template <typename T>
Something<T> F(const T&) { ... }
template <typename T, typename U, typename EnableIfNotT<U, T>*>
Something<T> F(U&&) { ... }
- 第二个重载永远不会优于第一个重载(因为它现在是转发引用重载,而不是右值引用重载),那么这里应该发生什么?
- 如果不明确指定
T
参数,似乎永远无法调用最后一个,这是预期的行为吗? - 在为 class 模板使用模板参数推导时,是否还有任何其他陷阱或风格指南需要牢记?
- class 定义之后还需要用户定义的推导指南吗?例如,您能否在 class 定义本身的 class 构造函数的声明中包含尾随 return 类型? (不像这里的迭代器构造函数http://en.cppreference.com/w/cpp/language/class_template_deduction)
- The second overload will never be preferred over the first one (since that is now a forwarding reference overload, instead of being an rvalue reference overload), so what is supposed to happen here?
不,它不是转发参考。这是一个关键的区别。来自 [temp.deduct.call]:
A forwarding reference is an rvalue reference to a cv-unqualified template parameter that does not represent a template parameter of a class template (during class template argument deduction ([over.match.class.deduct])).
您的候选人是:
template <typename T>
Something<T> F(T&&); // this ONLY matches non-const rvalues
template <typename T>
Something<T> F(const T&); // this matches everything
template <typename T, typename U, typename EnableIfNotT<U, T>*>
Something<T> F(U&&); // this matches nothing
当你写:
auto something = Something{std::shared_ptr<int>{}};
T&&
构造函数是首选,T=std::shared_ptr<int>
,所以你最终得到 Something<std::shared_ptr<int>>
作为你的 class 模板专业化。如果改写:
std::shared_ptr<int> p;
auto something = Something{p};
那么 T const&
构造函数是首选(实际上它是唯一可行的候选者)。虽然我们最终在同一个地方:Something<std::shared_ptr<int>>
.
- And it seems like the last one can never be called without explicitly specifying the T parameter, is this the intended behavior?
正确,T
是非推导上下文。这是有道理的——此构造函数的存在是为了进行转换,但您需要指定要将 转换为 的内容才能进行转换。为你准备这个 "just work" 是没有意义的。
- Further are user defined deduction guides required to be after the class definition?
是的。按照规则,这就是他们要去的地方。在构造函数中添加尾随 return 类型是没有意义的 - 构造函数没有 "return" 任何东西。