"anti-SFINAE"如果给定的表达式*格式不正确则启用重载

Aar*_*aid 5 c++ sfinae c++11

如果特定表达式格式不正确,则很容易使用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)

Yak*_*ont 5

这是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(功能较少,但实现更简单)。