0

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表达式的复杂性(因为它用于许多函数)。我在下面添加了一个答案,为此目的创建了一个自定义特征。

4

3 回答 3

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));
}
于 2013-09-27T07:46:30.447 回答
1

两个版本的主要区别在于 DT 在第二个版本中成为接口(模板参数)的一部分。如果此函数属于 api 接口并且设计中未指定 DT,则即使替代方法意味着更多代码,也不应使用第二个版本。但也许你可以在你的实现细节中编写一个包装器。

于 2013-09-27T06:33:33.627 回答
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)
{ }

即使 trait 只使用一次,也不比写一个包装模板函数差,如果多次使用,那么复杂的表达式会被保存以供每次重用。

于 2013-09-27T22:43:18.727 回答