如何在 T==void 时最好地解决 "void foo( const T& t = T() )"
How to best solve "void foo( const T& t = T() )" when T==void
我有一个函数,它有一个类型为 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 解决了很多问题!更简洁、更短的语法会更好...
Ideally I hope there should be a more elegant solution to overload the 'Void' function without resorting to a 3rd declaration? Especially with new C++17 solving so many things these days! A more concise syntax that was shorter coudl be nice...
嗯...没有第三个声明,是的(你只能使用一个)。
更优雅...我想这是品味问题。
更准确的语法...好吧...几乎相同,我想。
无论如何,我建议以下版本,if constexpr
和std::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; */ }
}
一个更简单的解决方案(因为只有 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() {}
两个默认模板参数即可:
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();
}
int&
是 T
的默认值,因此如果有人试图调用 foo()
时编译失败(default-constructing 在 U()
处的引用)模板参数或实际参数(尝试在示例中取消注释)。
我在 FallbackT
别名模板中使用 int
,因为 U
只需要是 default-constructible 的东西 - 这对用户不可见。
如果你想花哨(并防止误用),你可以添加可变参数保护并使用闭包类型:
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<int, long>()
;闭包类型使得某人无法通过任何其他方式使用这些类型调用 foo
- 这可能是不必要的。
我有一个函数,它有一个类型为 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 解决了很多问题!更简洁、更短的语法会更好...
Ideally I hope there should be a more elegant solution to overload the 'Void' function without resorting to a 3rd declaration? Especially with new C++17 solving so many things these days! A more concise syntax that was shorter coudl be nice...
嗯...没有第三个声明,是的(你只能使用一个)。
更优雅...我想这是品味问题。
更准确的语法...好吧...几乎相同,我想。
无论如何,我建议以下版本,if constexpr
和std::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; */ }
}
一个更简单的解决方案(因为只有 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() {}
两个默认模板参数即可:
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();
}
int&
是 T
的默认值,因此如果有人试图调用 foo()
时编译失败(default-constructing 在 U()
处的引用)模板参数或实际参数(尝试在示例中取消注释)。
我在 FallbackT
别名模板中使用 int
,因为 U
只需要是 default-constructible 的东西 - 这对用户不可见。
如果你想花哨(并防止误用),你可以添加可变参数保护并使用闭包类型:
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<int, long>()
;闭包类型使得某人无法通过任何其他方式使用这些类型调用 foo
- 这可能是不必要的。