C++ traits类检测成员函数: compiler error in MSVC 2015 RC

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

以下特征类用于检查通用 vector 是否具有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 中)?

最佳答案

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

这是表达 SFINAE 的当前状态,取自 here :

Q. When will you implement Expression SFINAE?

A. We're planning to start implementing Expression SFINAE in the compiler immediately after 2015 RTM, and we're planning to deliver it in an Update to 2015, supported for production use. (But not necessarily 2015 Update 1. It might take longer.)

To be extra clear, this applies to the compiler only, not the libraries. The C++11 Standard Library mandated the use of Expression SFINAE in a small number of places, which we've actually already implemented. (The places are is_assignable and allocator_traits, which the compiler has taken special care to support.) The C++14 Standard Library added two more places where Expression SFINAE must be used: std::function construction/assignment and result_of. I'll modify the STL when the compiler is ready, but I'm not planning to ship those library changes in an Update. ...



此外,这里有一个有点使用传统 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];
}

这会检查传递的 vector 类型是否有成员 double operator[](int) , 如果这样调用它(——否则可以用括号运算符做类似的事情)。



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

关于C++ traits类检测成员函数: compiler error in MSVC 2015 RC,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30856989/

相关文章:

templates - 如何在jinja2中追加到 list

c++ - vector 的排序 vector

c++ - 深拷贝和动态转换 unique_ptr

c++ - 按函数名参数化模板 c++11

c++ - 系统与 ShellExecute - 差异?

c++ - v8::ScriptCompiler::Source 对象 - 这可以保留或稍后检索吗?

c++ - WINAPI SetLastError 与 C++ Keword 抛出

c++ - c++ 源代码使用什么文件扩展名

c++ - 如何使用模板将 QString 转换为类型名?

c++ - 根据实例化点期望不同的类型