使用默认模板参数引入派生类型
Using default template parameters to introduce derived type
我有一个模板,我需要在其中多次使用从用户提供的类型派生的类型T
noexcept
说明符。 在下面的示例中,我需要多次使用衰减类型的T
。 幼稚的方法是:
// in this case
template <typename T>
void foo(T&& t) noexcept( noexcept(typename std::decay<T>::type(std::forward<T>(t)))
&& noexcept(std::declval<typename std::decay<T>::type>() = std::forward<T>(t)))
{
typedef typename std::decay<T>::type DT;
/* ... */
}
经常重复typename std::decay<T>::type
的需要似乎过于冗长。 我可以用 DT
类型替换整个表达式,但不幸的是,这仅在函数体中可用。
在我能想到的函数体之前引入DT
的最简单方法是:
template <typename T, typename DT = typename std::decay<T>::type>
void foo(T&& t) noexcept( noexcept(DT(std::forward<T>(t)))
&& noexcept(std::declval<DT>() = std::forward<T>(t)))
{
/* DT also available here... */
}
不过,仅为此目的添加另一个模板参数感觉是错误的事情。 所以,我的问题:
- 添加额外的默认模板参数是否有任何有害的副作用?
- 这算不算不好的做法?
- 有没有其他方法可以引入
DT
,以便在noexcept
说明符中可用?
编辑:在我看来,真正的问题不是获得对DT
的访问,而是完全降低noexcept
表达式的复杂性(因为它用于许多函数(。 我在下面添加了一个答案,用于为此目的创建自定义特征。
我无法回答第二个问题,即它是否被认为是不良做法,因为我认为这些都是根据具体情况做出的决定。对你有用的东西可能对其他人不起作用。
您应该了解的是替代方案。添加另一个默认模板参数会更改 API,如果调用方认为他需要显式提供这两个模板参数,则可能会出现错误。这不太可能,AFAIC通常不是一个大问题。API修改OTOH可能是一个问题。
在这种情况下,您可以考虑将 foo
转换为转发器以foo_impl
并且只有foo_impl
具有第二个默认模板参数。这通常有助于保持 API 干净。如果foo_impl
移动到匿名命名空间(如果该命名空间位于类内,则移动到 private
秒(,编译器可能会也可能不会内联它,甚至生成的代码看起来也像您的移动详细和显式版本。
namespace
{
template <typename T, typename DT = typename std::decay<T>::type>
void foo_impl(T&& t) noexcept( noexcept(DT(std::forward<T>(t)))
&& noexcept(std::declval<DT>() = std::forward<T>(t)))
{
/* DT also available here... */
}
}
// clean API
template <typename T>
void foo(T&& t) noexcept(noexcept(foo_impl(std::forward<T>(t))))
{
return foo_impl(std::forward<T>(t));
}
两个版本之间的主要区别在于DT在第二个版本中成为接口(模板参数(的一部分。如果此函数属于 API 接口,并且设计中未指定 DT,则不应使用第二个版本,即使替代版本意味着更多代码。但也许您可以在实现细节中编写一个包装器。
我只是想到了一个比我以前接受的解决方案更可行的解决方案。 与其在每次我有复杂的noexcept
表达式时创建一个包装器模板函数,我可以引入一个自定义特征。 这样我可以轻松地在多个函数上重用 noexcept
表达式,而不是为每个函数编写包装器:
// complex trait only has to be written once
template <typename T>
struct my_noexcept_trait : std::integral_constant<bool,
noexcept(typename std::decay<T>::type(std::forward<T>(t)))
&& noexcept(std::declval<typename std::decay<T>::type>() = std::forward<T>(t))>
{ };
// but can be used simply in many functions
template <typename T>
void foo(T&& t) noexcept(my_noexcpt_trait<T>::value)
{ }
template <typename T>
void bar(T&& t) noexcept(my_noexcept_trait<T>::value)
{ }
即使 trait 只使用一次,也不比编写包装器模板函数差,如果多次使用,则每次重用都会保存复杂的表达式。
- 在 C++ 中用派生类型重写成员函数
- 在 C++ 中将对象转换为派生类型
- C++如果采用类类型的函数被传递派生类型,有没有办法给出错误?
- 返回派生类型时出现协变类型错误
- 如何模板化堆栈分配的多态指针数组到接口,包括派生类型的相应点?
- 基于派生类型的编译时行为分支
- 按类型排序向量并按类型或派生类型搜索
- 为什么"运算符<<"不适用于指向派生类型的成员?
- 无法返回派生类型的标准::unique_ptr<>
- 无法从派生类型的作用域访问另一个实例的受保护成员
- dynamic_cast到具有未知模板参数的派生类型
- 派生类型的成员
- 视觉C++ dynamic_cast返回 NULL,尽管对象是派生类型
- 在运算符中动态强制转换为派生类型<<
- 如何在不转换回基类类型的情况下返回派生类型的对象?
- 将派生指针分配给C 中的其他派生类型
- 如何将派生类型的对象放在用于基本类型的向量中
- C++ - 无法将派生类型作为基类型列表传递给构造函数
- 如何在基本类型参考中存储对派生类型的参考
- 统一处理具有协变类型的函数指针(如何使用派生类型调用回调?