我有一个模板,我需要在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
表达式的复杂性(因为它在许多函数中使用)。我在下面添加了一个为此目的创建自定义特征的答案。
答案 0 :(得分:3)
我觉得我不能回答第二个问题,即它是否被认为是不好的做法,因为我认为这些是个案基础上的决定。什么对你有用可能不适合其他人。
你应该知道的是替代方案。添加另一个默认模板参数会更改API,如果调用者认为他需要明确提供两个模板参数,则可能会出现错误。这不太可能,AFAICS通常不是一个大问题。 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));
}
答案 1 :(得分:1)
两个版本之间的主要区别在于DT成为第二个版本中接口(模板参数)的一部分。 如果此函数属于api接口且未在设计中指定DT,则不应使用第二个版本,即使替代方法意味着更多代码。 但也许您可以在实现细节中编写包装器。
答案 2 :(得分:1)
我只是想到了一个比我之前接受过的更可行的解决方案。我每次都有复杂的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)
{ }
即使特征仅使用一次,也不比编写包装器模板函数差,如果多次使用它,则会为每次重用保存复杂表达式。