【问题标题】:SFINAE issue with traits detecting divisionSFINAE 特征检测部门的问题
【发布时间】:2018-02-16 11:18:41
【问题描述】:

我有以下特征来检测两个类型是否可整除,否则返回结果操作类型或另一个Default 类型:

struct default_t { };

// Overloaded template for fallbacks
template <class U, class V>
default_t operator/(U, V);

// If the expression std::declval<U>()/std::declval<V>() is valid,
// gives the return type, otherwize provide Default.
template <class U, class V, class Default>
struct div_type_or {
    using type_ = decltype(std::declval<U>() / std::declval<V>());
    using type = typename std::conditional<
        std::is_same<type_, default_t>{},
            Default,
            type_>::type;
};

template <class... Args>
using div_type_or_t = typename div_type_or<Args...>::type;

这适用于 libstdc++,但不适用于 libc++,当我尝试使用不可分割的 std::chrono::duration 类型时,例如:

struct A { };

div_type_or_t<std::chrono::seconds, A, int> b;

我收到以下错误:

/Library/Developer/CommandLineTools/usr/include/c++/v1/chrono:764:81: 错误:没有名为“类型”的类型 'std::__1::common_type' 类型名 common_type::type>::value> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~^~~~

/Library/Developer/CommandLineTools/usr/include/c++/v1/chrono:777:7: 注意:在默认参数的实例化中 '__duration_divide_imp >, A>' 这里需要 : __duration_divide_imp, _Rep2> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~ >

/Library/Developer/CommandLineTools/usr/include/c++/v1/chrono:784:10: 注意:在模板类的实例化中 'std::__1::chrono::__duration_divide_result >, A, false>' 在此处请求类型名 __duration_divide_result, _Rep2>::type ^

test.cpp:16:46: 注意:将推导的模板参数替换为函数模板'operator/' [with _Rep1 = long 长,_Period = std::__1::ratio, Rep2 = A] 使用类型 = decltype(std::declval() / std::declval()); ^

test.cpp:24:1: 注意:在模板类的实例化中 'div_type_or >, A, D>' 在这里使用 div_type_or_t = typename div_type_or::type; ^

test.cpp:48:19: 注意:在模板类型别名“div_type_or_t”的实例化中 在这里请求 div_type_or_t, D>{}, "");

据我了解,这是因为 operator/std::chrono::duration 的以下重载失败:

template< class Rep1, class Period, class Rep2 >
duration<typename std::common_type<Rep1,Rep2>::type, Period>
    constexpr operator/( const duration<Rep1, Period>& d,
                         const Rep2& s );

我认为由于 std::common_type 是函数签名的一部分,这将允许 SFINAE 工作并使用我的自定义重载 operator/ 并因此推断出 default_t,但显然这不起作用...

由于这适用于 libstdc++,我只是想知道我的代码是否存在问题(也许我不明白 SFINAE 在这种情况下的工作方式)还是 libc++ 错误?

【问题讨论】:

  • 成为函数签名的一部分并不是让 SFINAE 正常工作的唯一要求。

标签: c++ c++14 libc++


【解决方案1】:

在 libc++ 中,我们有:

template <class _Duration, class _Rep, bool = __is_duration<_Rep>::value>
struct __duration_divide_result
{
};

template <class _Duration, class _Rep2,
    bool = is_convertible<_Rep2,
                          typename common_type<typename _Duration::rep, _Rep2>::type>::value>
struct __duration_divide_imp
{
};

template <class _Rep1, class _Period, class _Rep2>
struct __duration_divide_imp<duration<_Rep1, _Period>, _Rep2, true>
{
    typedef duration<typename common_type<_Rep1, _Rep2>::type, _Period> type;
};

template <class _Rep1, class _Period, class _Rep2>
struct __duration_divide_result<duration<_Rep1, _Period>, _Rep2, false>
    : __duration_divide_imp<duration<_Rep1, _Period>, _Rep2>
{
};

template <class _Rep1, class _Period, class _Rep2>
inline _LIBCPP_INLINE_VISIBILITY
_LIBCPP_CONSTEXPR
typename __duration_divide_result<duration<_Rep1, _Period>, _Rep2>::type
operator/(const duration<_Rep1, _Period>& __d, const _Rep2& __s)
{
    typedef typename common_type<_Rep1, _Rep2>::type _Cr;
    typedef duration<_Cr, _Period> _Cd;
    return _Cd(_Cd(__d).count() / static_cast<_Cr>(__s));
}

所以不是std::common_type&lt;_Rep1, _Rep2&gt;::type,
他们使用__duration_divide_result&lt;duration&lt;_Rep1, _Period&gt;, _Rep2 /*, false*/&gt;,
实例化__duration_divide_imp<duration<_Rep1, _Period>, _Rep2 /*, is_convertible<_Rep2, typename common_type<_Rep1, _Rep2>::type>::value*/>

这种用法是一个硬错误。

我会说 libc++ 中的实现在这方面是不正确的。

【讨论】:

  • 感谢您的回答。您是否认为有一种简单的方法可以以通用方式解决此问题,还是我应该尝试将我的特质专门用于std::chrono::duration
  • std::chrono::seconds / A 目前在 libc++ 中对 SFINAE 不友好。作为解决方法,您可能确实添加了default_t operator/(std::chrono::seconds, A);
猜你喜欢
  • 2017-03-06
  • 2017-07-25
  • 2015-05-27
  • 2023-03-18
  • 2012-08-21
  • 2017-02-23
  • 1970-01-01
  • 1970-01-01
  • 2011-01-15
相关资源
最近更新 更多