SFINAE enable/disable 函数和模板别名
SFINAE enable/disable function and template alias
我想知道如何实现我在下面描述的内容。
考虑一个基本 CRTP class,其中一个功能需要启用而另一个功能需要禁用。
这是通过特征 class 控制的,具体取决于类型的定义。
我最好不想在我的特征 class 中定义任何东西,但如果它有助于将其定义为 void 我可以接受。更多信息在评论中,因为在那里更容易展示我想要实现的目标。
template<class T>
struct Model_Traits;
// Base CTRP class
template<typename Model>
class BaseModel
{
inline const Model& impl() const { return static_cast<Model const&>(*this); }
// not sure how to enable this template or not based on my traits class
// which is necessary as some wont have it defined
using foo_t = typename Model_Traits<Model>::foo_t;
// this one should be enabled when foo_t is not defined
void optimize(const foo1& f1, const foo2& f2, foo3* f3)
{
impl().optimize(f1,f2,f3);
}
// this one should only be enabled if foo_t is defined
// not sure if this is correct
template<typename T = foo_t,
typename = std::enable_if<foo_t>::type>
void optimize(const foo1& f1, const foo2& f2, foo3* f3, foo_t* f4)
{
impl().optimize(f1,f2,f3, f4);
}
}
// First class defining the foo_t
template<MyModel>
struct Model_Traits
{
using foo_t = myFoo;
}
class MyModel : public BaseModel<MyModel>
{
void optimize(const foo1& f1, const foo2& f2, foo3* f3, foo_t* f4);
}
// Second class not defining foo_t
template<MyModel2>
struct Model_Traits
{
}
class MyModel2 : public BaseModel<MyModel2>
{
void optimize(const foo1& f1, const foo2& f2, foo3* f3);
}
我当然应该说这是简化的,我的代码看起来不像那样,但如果你去掉所有其他东西,它非常接近。
有什么想法吗?
您的代码中存在概念性错误,因为您将类型输入 std::enable_if
class 的第一个参数(而它应该是布尔值):
typename = std::enable_if<foo_t>::type
...
(而且std::enable_if
前面少了一个typename
)
因此,您的特征 class 应该包含一个 static constexpr bool
来表示是否应该启用该功能,而不是类型:
static constexpr bool enable_foo = Model_Traits<Model>::enable_foo;
然后您可以将其传递给 std::enable_if
(或更方便:std::enable_if_t
——该别名让您忘记 typename
和 ::type
)。
通过从 `std::enable_if` 派生
另一个直接的解决方法是从 std::enable_if
:
派生你的 Traits-classes
template<T>
struct Model_Traits : public std::enable_if<false> {}
template<>
struct Model_Traits<MyModel> : public std::enable_if<true, myFoo> {}
template<>
struct Model_Traits<MyModel2> : public std::enable_if<false>
{
//in principle you don't need to restate that, just for clarity
}
然后通过
使用它
template<typename T = foo_t,
typename = typename Model_Traits<T>::type>
void optimize(const foo1& f1, const foo2& f2, foo3* f3, foo_t* f4)
{
impl().optimize(f1,f2,f3, f4);
}
这将只为 MyModel
启用该功能,而不为 MyModel2
启用该功能。
使用`void_t`
此外,如评论中所述,在更现代的风格中,您还可以使用 void_t
:
template<class ...> using void_t = void;
struct A {};
struct B
{
using foo_t=double;
};
struct C
{
template<typename T, typename = void_t<typename T::foo_t> >
void foo(T const& t)
{
std::cout<<"hello"<<std::endl;
}
};
int main()
{
//C{}.foo(A{}); //compile time-error
C{}.foo(B{}); //ok, prints "hello"
}
这可能是最直接的选择。另见 some related threads on SO.
我们可以通过几个步骤完成此操作。首先,如果 foo_t
是使用 void_t
:
定义的,则创建一个类型特征
template <typename...>
using void_t = void;
template <typename T, typename = void>
struct has_foo_t : std::false_type { };
template <typename T>
struct has_foo_t<T, void_t<typename T::foo_t>> : std::true_type { };
然后我们将使用它来将我们的 optimize
函数转发到 SFINAE 友好上下文中的东西:
template <typename Model>
class BaseModel {
public:
template <typename... Args>
void optimize(Args&&... args) {
optimize_impl<Model>(std::forward<Args>(args)...);
}
};
我们将有两个 impl
函数:一个 enable
d 和一个 disable
d:
template <typename T,
typename = std::enable_if_t<has_foo_t<T>::value>>
void optimize_impl(const foo1& f1, const foo2& f2, foo3* f3, typename T::foo_t* f4)
{ .. }
template <typename T,
typename = std::enable_if_t<!has_foo_t<T>::value>>
void optimize_impl(const foo1& f1, const foo2& f2, foo3* f3)
{ .. }
即使在这种情况下 T
是 Model
,我们仍然需要它作为函数模板参数,以便允许替换失败是 "soft" 失败而不是硬编译错误.
你可以创造你的特质:
namespace detail
{
template <typename T>
decltype(std::declval<typename T::foo_t>(), std::true_type{}) has_foo_t_impl(int);
template <typename T>
std::false_type has_foo_t_impl(...);
}
template <typename T>
using has_foo_t = decltype(detail::has_foo_t_impl<T>(0));
然后创建一个助手class:
template<typename Model, bool = has_foo_t<Model>>
struct BaseModelOptimizeHelper;
template<typename Model>
struct BaseModelOptimizeHelper<Model, true>
{
void optimize_impl(const Model& model, const foo1& f1, const foo2& f2, foo3* f3, foo_t* f4){
model.optimize(f1,f2,f3, f4);
}
};
template<typename Model>
struct BaseModelOptimizeHelper<Model, false>
{
void optimize_impl(const Model& model, const foo1& f1, const foo2& f2, foo3* f3)
{
model.optimize(f1,f2,f3);
}
};
最后:
template<typename Model>
class BaseModel : protected BaseModelOptimizeHelper<Model>
{
const Model& impl() const { return static_cast<Model const&>(*this); }
public:
template <typename ... Ts>
void optimize(Ts&&... ts) { optimize_impl(impl(), std::forward<Ts>(ts)...); };
};
我想知道如何实现我在下面描述的内容。
考虑一个基本 CRTP class,其中一个功能需要启用而另一个功能需要禁用。
这是通过特征 class 控制的,具体取决于类型的定义。
我最好不想在我的特征 class 中定义任何东西,但如果它有助于将其定义为 void 我可以接受。更多信息在评论中,因为在那里更容易展示我想要实现的目标。
template<class T>
struct Model_Traits;
// Base CTRP class
template<typename Model>
class BaseModel
{
inline const Model& impl() const { return static_cast<Model const&>(*this); }
// not sure how to enable this template or not based on my traits class
// which is necessary as some wont have it defined
using foo_t = typename Model_Traits<Model>::foo_t;
// this one should be enabled when foo_t is not defined
void optimize(const foo1& f1, const foo2& f2, foo3* f3)
{
impl().optimize(f1,f2,f3);
}
// this one should only be enabled if foo_t is defined
// not sure if this is correct
template<typename T = foo_t,
typename = std::enable_if<foo_t>::type>
void optimize(const foo1& f1, const foo2& f2, foo3* f3, foo_t* f4)
{
impl().optimize(f1,f2,f3, f4);
}
}
// First class defining the foo_t
template<MyModel>
struct Model_Traits
{
using foo_t = myFoo;
}
class MyModel : public BaseModel<MyModel>
{
void optimize(const foo1& f1, const foo2& f2, foo3* f3, foo_t* f4);
}
// Second class not defining foo_t
template<MyModel2>
struct Model_Traits
{
}
class MyModel2 : public BaseModel<MyModel2>
{
void optimize(const foo1& f1, const foo2& f2, foo3* f3);
}
我当然应该说这是简化的,我的代码看起来不像那样,但如果你去掉所有其他东西,它非常接近。
有什么想法吗?
您的代码中存在概念性错误,因为您将类型输入 std::enable_if
class 的第一个参数(而它应该是布尔值):
typename = std::enable_if<foo_t>::type ...
(而且std::enable_if
前面少了一个typename
)
因此,您的特征 class 应该包含一个 static constexpr bool
来表示是否应该启用该功能,而不是类型:
static constexpr bool enable_foo = Model_Traits<Model>::enable_foo;
然后您可以将其传递给 std::enable_if
(或更方便:std::enable_if_t
——该别名让您忘记 typename
和 ::type
)。
通过从 `std::enable_if` 派生
另一个直接的解决方法是从 std::enable_if
:
template<T>
struct Model_Traits : public std::enable_if<false> {}
template<>
struct Model_Traits<MyModel> : public std::enable_if<true, myFoo> {}
template<>
struct Model_Traits<MyModel2> : public std::enable_if<false>
{
//in principle you don't need to restate that, just for clarity
}
然后通过
使用它 template<typename T = foo_t,
typename = typename Model_Traits<T>::type>
void optimize(const foo1& f1, const foo2& f2, foo3* f3, foo_t* f4)
{
impl().optimize(f1,f2,f3, f4);
}
这将只为 MyModel
启用该功能,而不为 MyModel2
启用该功能。
使用`void_t`
此外,如评论中所述,在更现代的风格中,您还可以使用 void_t
:
template<class ...> using void_t = void;
struct A {};
struct B
{
using foo_t=double;
};
struct C
{
template<typename T, typename = void_t<typename T::foo_t> >
void foo(T const& t)
{
std::cout<<"hello"<<std::endl;
}
};
int main()
{
//C{}.foo(A{}); //compile time-error
C{}.foo(B{}); //ok, prints "hello"
}
这可能是最直接的选择。另见 some related threads on SO.
我们可以通过几个步骤完成此操作。首先,如果 foo_t
是使用 void_t
:
template <typename...>
using void_t = void;
template <typename T, typename = void>
struct has_foo_t : std::false_type { };
template <typename T>
struct has_foo_t<T, void_t<typename T::foo_t>> : std::true_type { };
然后我们将使用它来将我们的 optimize
函数转发到 SFINAE 友好上下文中的东西:
template <typename Model>
class BaseModel {
public:
template <typename... Args>
void optimize(Args&&... args) {
optimize_impl<Model>(std::forward<Args>(args)...);
}
};
我们将有两个 impl
函数:一个 enable
d 和一个 disable
d:
template <typename T,
typename = std::enable_if_t<has_foo_t<T>::value>>
void optimize_impl(const foo1& f1, const foo2& f2, foo3* f3, typename T::foo_t* f4)
{ .. }
template <typename T,
typename = std::enable_if_t<!has_foo_t<T>::value>>
void optimize_impl(const foo1& f1, const foo2& f2, foo3* f3)
{ .. }
即使在这种情况下 T
是 Model
,我们仍然需要它作为函数模板参数,以便允许替换失败是 "soft" 失败而不是硬编译错误.
你可以创造你的特质:
namespace detail
{
template <typename T>
decltype(std::declval<typename T::foo_t>(), std::true_type{}) has_foo_t_impl(int);
template <typename T>
std::false_type has_foo_t_impl(...);
}
template <typename T>
using has_foo_t = decltype(detail::has_foo_t_impl<T>(0));
然后创建一个助手class:
template<typename Model, bool = has_foo_t<Model>>
struct BaseModelOptimizeHelper;
template<typename Model>
struct BaseModelOptimizeHelper<Model, true>
{
void optimize_impl(const Model& model, const foo1& f1, const foo2& f2, foo3* f3, foo_t* f4){
model.optimize(f1,f2,f3, f4);
}
};
template<typename Model>
struct BaseModelOptimizeHelper<Model, false>
{
void optimize_impl(const Model& model, const foo1& f1, const foo2& f2, foo3* f3)
{
model.optimize(f1,f2,f3);
}
};
最后:
template<typename Model>
class BaseModel : protected BaseModelOptimizeHelper<Model>
{
const Model& impl() const { return static_cast<Model const&>(*this); }
public:
template <typename ... Ts>
void optimize(Ts&&... ts) { optimize_impl(impl(), std::forward<Ts>(ts)...); };
};