我有一个函数,它有一个类型为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解决了很多问题!一个更简洁、更短的语法可能会很好。。。
一个更简单的解决方案(因为只有两个重载(应该是这样的:
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()
(请尝试在示例中取消注释(,则编译失败(默认情况下在U()
处构造引用(。
我在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<int, long>()
;闭包类型使得人们不可能通过任何其他方式用这些类型调用foo
——这可能是不必要的。
理想情况下,我希望应该有一个更优雅的解决方案来重载"Void"函数,而不需要使用第三个声明?尤其是现在新的C++17解决了很多问题!一个更简洁、更短的语法可能会很好。。。
嗯。。。如果没有第三个声明,是的(您只能使用一个(。
更优雅。。。我想这是品味的问题。
更精确的语法。。。好我想差不多。
无论如何,我提出以下版本,基于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; */ }
}