如果特定表达式格式不正确,则很容易使用SFINAE来隐藏特定的函数重载.但我想反其道而行之,隐藏过载,当且仅当一个表达式是良好的,并在一个非常普遍的方式这样做.我有一个在clang 3.5.0和gcc 5.2.0中工作的解决方案,但我对任何评论和替代方案感兴趣.
理想情况下,会有一个内置constexpr bool函数/宏在编译时告诉我们特定表达式是否形成良好.
IS_WELL_FORMED( declval<T>() < declval<T>() ) // I want this as bool
Run Code Online (Sandbox Code Playgroud)
可用于enable_if启用或禁用重载.
我找到了一个解决方案,但是我在g ++ 5.2.0和clang 3.5.0中遇到了一些奇怪的行为,我想知道是否有错误.
首先,我到目前为止找到的最强大的解决方案,适用于两个编译器.例如,我想测试是否T有.length()方法.这需要将表达式"隐藏"在另一个模板中.此外,well_formed_default我将在稍后讨论一个函数.
// Define a template to contain our expression
template<typename T2=T, typename =
decltype( declval<T2>().length() ) // This line is the expression to test
> struct test_for_length_method { };
Run Code Online (Sandbox Code Playgroud)
以下是它在包含类中的使用方式:
template<typename T>
struct Demo { // the main struct I'm working on
// Define a template to "hide" our expression
template<typename T2=T, typename =
decltype( declval<T2>().length() ) // This line is the expression to test
> struct test_for_length_method { };
// test if the above "test" succeeds
constexpr bool T_has_length =
well_formed_default< test_for_length_method >();
// demonstrate the bool in an enable_if
template<bool b = T_has_length>
static
auto demo_enable_if() -> typename std::enable_if< b >::type {
cout << "T has length" << endl;
}
template<bool b = T_has_length>
static
auto demo_enable_if() -> typename std::enable_if< !b >::type {
cout << "T doesn't" << endl;
}
}
Run Code Online (Sandbox Code Playgroud)
以上工作与Demo<int>::demo_enable_if()和预期的一样Demo<std::string>::demo_enable_if().
我不能T_has_length直接在里面使用,enable_if因为它不会导致硬错误,因为它不是模板替换.因此,我通过在另一个模板参数中复制if来假装它是一个模板参数bool b = T_has_length.这类似于我们typename T2=T在测试结构中使用的方式.有点烦人,但我觉得这很有道理.
我现在定义well_formed_default.它需要一个模板(以及可选的某些类型)并返回true或false,具体取决于它是否可以使用这些特定的args构造模板.我会在我的所有项目中自动包含它.也许这样的事情已经存在于标准中?
template<template<class...> class Template, class ...Args>
constexpr auto well_formed_default_impl(int)
-> typename std::conditional<false,Template<Args...>, bool>::type {
return true;
}
template<template<class...> class Template, class ...Args>
constexpr auto well_formed_default_impl(...)
-> bool {
return false;
}
template<template<class...> class Template, class ...Args>
constexpr bool well_formed_default() {
return well_formed_default_impl<Template,Args...>(0);
}
Run Code Online (Sandbox Code Playgroud)
-
这适用于g ++ 5.2.0和clang 3.5.0.但是应该吗?这是完全标准的,还是我推动标准太过分了?我想对我来说最奇怪的是使用Template<Args...>内部well_formed_default_impl- 这是否保证在我使用它的方式中取代失败?例如,test_for_pushback_method_struct<>当相关decltype性不完善时?
-
(对于这个问题的其余部分,可能有助于查看Coliru上此代码的输出,因为它具有我在下面讨论的所有测试和结果.)
我用别名而不是上面的结构开始了这个项目.我认为这是等价的.而是用两种编译器,他们认为string它不具有的长度方法.
template<typename T2=T>
using test_for_length_method_alias = decltype( declval<T2>().length() );
Run Code Online (Sandbox Code Playgroud)
最后,我尝试了struct和别名,但是我明确定义了第一个类型参数(T)而不是依赖于默认值T2=T.这不应该改变任何东西,因为我传递的是默认类型 - 但它确实改变了行为!使用带有显式第一个参数的struct
well_formed_default<test_for_length_method_struct , T>
Run Code Online (Sandbox Code Playgroud)
在两个编译器上都能正常工作.但是alias-with-explicit-first-type只能与clang一起正常工作:
well_formed_default<test_for_length_method_alias , T>
Run Code Online (Sandbox Code Playgroud)
这是can_apply样板:
template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;
namespace details {
template<template<class...>class Z, class, class...>
struct can_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, void_t<Z<Ts...>>, Ts...>:
std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply=details::can_apply<Z, void, Ts...>;
Run Code Online (Sandbox Code Playgroud)
对于特定问题,如果x<y使用它看起来像:
template<class Lhs, class Rhs>
using less_r = decltype( std::declval<Lhs>() < std::declval<Rhs>() );
template<class Lhs, class Rhs>
using can_less = can_apply< less_r, Lhs, Rhs >;
Run Code Online (Sandbox Code Playgroud)
然后使用can_less:
struct Foo {};
struct Bar {};
void operator<( Foo, Bar ) {}
int main() {
std::cout << can_less<Foo, Bar>{} << can_less<Bar,Foo>{} << can_less<int, int>{} << can_less<char*, int>{} << '\n';
}
Run Code Online (Sandbox Code Playgroud)
输出1010在 clang 和 gcc 中。
这在标准下工作,并且它大致匹配 a 的接口std::experimental::is_detected(功能较少,但实现更简单)。
| 归档时间: |
|
| 查看次数: |
348 次 |
| 最近记录: |