【问题标题】:How to best solve "void foo( const T& t = T() )" when T==void当 T==void 时如何最好地解决“void foo(const T& t = T())”
【发布时间】:2021-02-18 09:59:19
【问题描述】:

我有一个函数,它有一个 T 类型的选项参数。

template<typename T>  
void foo( const T& t = T() )  
{ t.stuff; }

这一切都很好,但我现在有一个 T 变成 void 的场景。在这种情况下,我期望一个无操作的空函数。我唯一可行的解​​决方案需要三个单独的声明,并且我有许多这样的方法:

template<typename T> 
void foo( const T& t)
{ t.stuff; }

template<typename T>
inline void foo() 
{ foo(T()); }

template<> 
inline void foo<void>() {}

理想情况下,我希望应该有一个更优雅的解决方案来重载“Void”函数而不诉诸第三个声明?尤其是现在新的 C++17 解决了很多事情!更简洁的语法更短可能会很好......

【问题讨论】:

  • 如果Tvoid,那么t.stuff() 应该做什么?似乎我们想要编译器错误。你真的打算打电话给foo 是一个无操作的人吗?如果是这样,也许我们应该重新审视你的设计。

标签: c++ templates c++14 c++17 void


【解决方案1】:

两个默认的模板参数可以做到这一点:

template<class T> using FallbackT = std::conditional_t<std::is_void_v<T>, int, T>;

template<class T = int&, class U = FallbackT<T>>
void foo(U const& t = U()) {
    if constexpr (!std::is_void_v<T>)
        t.stuff();
}

Example.

int&amp;T 的默认值,因此如果有人尝试在没有模板参数或实际参数的情况下调用 foo()(尝试在例子)。

我在 FallbackT 别名模板中使用 int,因为 U 只需要是默认可构造的 - 这对用户不可见。

如果你想花哨(并防止误用),你可以添加一个可变参数守卫并使用闭包类型:

template<
    class T = decltype([]{})&,
    class...,
    class U = std::conditional_t<std::is_void_v<T>, decltype([]{}), T>>
void foo(U const& t = U()) {
    if constexpr (!std::is_void_v<T>)
        t.stuff();
}

在这里,可变参数保护防止明确指定 U,例如foo&lt;int, long&gt;();闭包类型使某人无法通过任何其他方式使用这些类型调用 foo - 这可能是不必要的。

【讨论】:

  • 感谢您的精彩回答。第一个版本解决了用例,但是对于许多功能来说它非常冗长。如果有一种语法可以简化所涉及的复制粘贴的复杂性以及随后的错误机会,那就太好了! 'Fancy' 版本需要 -std=C++2a 功能,很高兴看到,但我并不清楚这可以/将避免哪些误用?
  • 当然,我尽可能地缩短了它——第二个模板参数需要某种别名模板。我还添加了对“花哨”功能/技术的解释。
【解决方案2】:

一个更简单的解决方案(因为只有 2 个重载)是这样的:

template<typename T>
void foo( const T& t = T() ) {
    t.stuff;
}

template<typename T>
std::enable_if_t<std::is_void_v<T>>
foo() {}

// void foo(const T&); is the only viable overload for non-void T,
// since std::enable_if_t SFINAEs

// void foo(); is the only viable overload for void T,
// since const T& forms a reference to void

由于您经常使用这种模式,因此可以使用别名模板稍微缩短:

template<typename T, typename TypeIfVoid = void>
using if_void = std::enable_if_t<std::is_void_v<T>, TypeIfVoid>;


template<typename T>
void foo(const T& t = T()) {
    t.stuff;
}

template<typename T>
if_void<T> foo() {}

【讨论】:

  • 第二个版本真正适合具有许多功能的用例母鸡。我试图实现这一点,但我的语法错误,所以感谢您帮助解决这个问题:) 还有其他带有单个函数定义的答案,但它们都非常冗长,无法阅读+使用,所以我会接受这个答案,因为它清晰简洁.谢谢
【解决方案3】:

理想情况下,我希望应该有一个更优雅的解决方案来重载“Void”函数而不诉诸第三个声明?尤其是现在新的 C++17 解决了很多事情!更简洁的语法更短可能会很好......

嗯...没有第三个声明,是的(你只能使用一个)。

更优雅……我想这是品味的问题。

更简洁的语法......嗯......几乎一样,我想。

无论如何,我建议使用以下版本,基于if constexprstd::conditional_t

template <typename T,
          typename U = std::conditional_t<std::is_same_v<T, void>,
                                          int,
                                          T>>
void foo (U const & u = U{})
 {
   if constexpr ( std::is_same_v<T, void> )
    { /* do nothing */ }
   else
    { /* do u.stuff; */ }
 }

【讨论】:

  • 这样做的一个问题是必须提供T(并且U 可以推导出为不同的东西)。当然,如果它从未像 foo(value);foo&lt;base_class&gt;(derived_object); 那样被调用,那当然不是问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-18
  • 1970-01-01
  • 2021-04-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多