C ++ traits类来检测成员函数:MSVC 2015 RC中的编译器错误

时间:2015-06-16 00:20:42

标签: c++ templates c++11 visual-studio-2015 sfinae

以下特征类用于检查通用矢量是否具有operator[](int)operator()(int)

template<typename ...> using void_t = void;

template< typename, typename = void>
struct has_bracket_operator : public std::false_type {};

template<typename V>
struct has_bracket_operator<V, void_t<decltype(std::declval<V>()[int{}])> >
    : public std::true_type{};

template<typename, typename = void>
struct has_parenthesis_operator : public std::false_type {};

template<typename V>
struct has_parenthesis_operator<V, void_t<decltype(std::declval<V>()(int{}))> >
    : public std::true_type{};

似乎compile well in gcc 5.1.0

另一方面,在最新的MSVC下载( Microsoft Visual Studio Community 2015 RC版本14.0.22823.1 D14REL )中,我收到has_parenthesis_operator模板的编译器错误,其中包含:

error C2064: term does not evaluate to a function taking 1 arguments

此外,以下我认为最好的版本都不会编译(注意operator[](int{})代替[int{}],类似于括号):

template<typename V>
struct has_bracket_operator<V, void_t<decltype(std::declval<V>().operator[](int{}))> >
    : public std::true_type{};

//...

template<typename V>
struct has_parenthesis_operator<V, void_t<decltype(std::declval<V>().operator()(int{}))> >
    : public std::true_type{};

这里的错误信息是

error C2228: left of '.[]' must have class/struct/union
note: type is 'add_rvalue_reference<V>::type'
error C2228: left of '.()' must have class/struct/union
note: type is 'add_rvalue_reference<V>::type'

问题:

  • 这是一个错误吗?或者我做错了什么(gcc接受哪种方式)?
  • 是否有解决方法可以使用上述技术(尤其是void_t
  • 如果无法解决方法,如何在MSVC中设置备用SFINAE检查(我尝试了大部分this但未真正成功 - 至少在MSVC中的 )?

1 个答案:

答案 0 :(得分:0)

正如@ T.C。所述,上述方法void_t<decltype(...)>需要expression SFINAE,这在当前版本的MSVC 2015中不可用。

以下是表达式SFINAE的实现状态,取自here

  

Q值。你什么时候实施表达SFINAE?

     

一个。我们正计划开始实施表达SFINAE   2015 RTM之后立即编译,我们正计划交付它   在2015年更新中,支持生产使用。 (但不是   必然2015年更新1.可能需要更长时间。)

     

要清楚,这仅适用于编译器,而不适用于编译器   库。 C ++ 11标准库强制要求使用Expression   SFINAE在少数几个地方,我们已经实际上已经存在   实现。 (这些地方是is_assignable和allocator_traits,   编译器特别注意支持。)C ++ 14   标准库增加了两个表达SFINAE必须的地方   used:std :: function construction / assignment和result_of。我&#39; 11   编译器准备好后修改STL,但我不打算这样做   在更新中发送这些库更改。   ...


此外,对于使用传统SFINAE(灵感来自here)的上述特征,这里有一个有点更老式的解决方法:

template<typename C, typename Ret, typename... Args>
struct has_parenthesis_operator<C, Ret(Args...)>
{
private:
    template<typename T>
    static constexpr auto check(T*) 
        -> typename std::is_same<std::decay_t<decltype(std::declval<T>().operator()(std::declval<Args>()...))>, Ret>::type;

    template<typename> static constexpr std::false_type check(...);

    typedef decltype(check<C>(0)) type;

public:
    static constexpr bool value = type::value;
};

可以用作例如

template<typename V
       , typename = std::enable_if_t<has_bracket_operator<V, double(int)>::value>
auto get(V const& v, int i)
{
    return v[i];
}

检查传递的矢量类型是否有成员double operator[](int),如果是,则调用它( - 另外一个可以用括号运算符表示)。


通过@Yakk来评论评论:我遇到了MSVC的__if_exists statement特殊功能,它似乎完成了与上述类似的任务。