【问题标题】:noexcept + declval fail to compile under MSVCnoexcept + declval 在 MSVC 下编译失败
【发布时间】:2020-05-12 12:32:23
【问题描述】:

我正在尝试实现对我的 SO 问题 here 的回答: 我的目标是检测模板类T 中是否存在void cancel() noexcept 方法。 这是我的最小示例:

#include <iostream>
#include <type_traits>

template<class T, class = void>
struct has_cancel : std::false_type {};

template<class T>
struct has_cancel<T,
    typename std::void_t<decltype(std::declval<T>().cancel()),
    typename std::enable_if_t<noexcept(std::declval<T>().cancel())>
    >
> : std::true_type {};

void print_has_cancel(std::true_type) {
    std::cout << "cancel found" << std::endl;
}

void print_has_cancel(std::false_type) {
    std::cout << "cancel not found" << std::endl;
}

struct A{

};

struct B {
    void cancel(){}
};

struct C {
    int cancel() noexcept {}
};

struct D{
    void cancel() noexcept {}
};

int main(){
    print_has_cancel(has_cancel<A>());
    print_has_cancel(has_cancel<B>());
    print_has_cancel(has_cancel<C>());
    print_has_cancel(has_cancel<D>());
    return 0;
}

Visual Studio 社区 2019 的输出:

1>------ Build started: Project: cpp_sandbox, Configuration: Debug x64 ------
1>cpp_sandbox.cpp
1>C:\Users\David Haim\source\repos\cpp_sandbox\cpp_sandbox\cpp_sandbox.cpp(10,1): error C2228:  left of '.cancel' must have class/struct/union
1>C:\Users\David Haim\source\repos\cpp_sandbox\cpp_sandbox\cpp_sandbox.cpp(10,1): message :  type is '_Add_reference<_Ty,void>::_Rvalue'
1>C:\Users\David Haim\source\repos\cpp_sandbox\cpp_sandbox\cpp_sandbox.cpp(10,19): error C2056:  illegal expression
1>Done building project "cpp_sandbox.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

经过一番研究,问题在于语法noexcept(std::declval&lt;type&gt;().something()) - MSVC 拒绝理解这一行。

1) 为什么 MSVC 抱怨?是代码的bug还是编译器的bug?

2) 我怎样才能让它编译?

【问题讨论】:

  • 我相信一些东西,还没有在 VS 中实现。但是,旧的 SIFANE 技巧可能会有所帮助?见this
  • @P0W 它没有检测到 cancel 是否为 noexcept。我可以接受任何版本的标准,只要该技巧也可以检测到 noexcept。

标签: c++ templates visual-c++ compiler-errors sfinae


【解决方案1】:

MSVC 本身的语法没有问题。似乎只是假设 SFINAE 在这种情况下不适用,因此将 void 替换为类型会给出错误。标准中为什么或是否对此有任何支持,我目前不知道(但我对此表示怀疑)。

这可以通过将noexcept 条件从声明中移动到struct 的定义中轻松解决,这样如果第一个decltype 格式错误(即如果.cancel()根本不存在)和 SFINAE 介入:

template<class T>
struct has_cancel<T,
    typename std::void_t<decltype(std::declval<T>().cancel())    >
> : std::bool_constant<noexcept(std::declval<T>().cancel())> {};

【讨论】:

  • 我查了 en.cppreference.com/w/cpp/types/void_t 并发现:“直到 CWG 1558(一个 C++14 缺陷),别名模板中未使用的参数不能保证确保 SFINAE 并且可以被忽略”。跨度>
  • @Frodyne 虽然这似乎不是 MSVC 所考虑的。即使删除了void_t 并且只使用了一个简单的enable_if_t,它仍然会产生相同的错误:godbolt.org/z/W3gT7T
猜你喜欢
  • 2012-04-13
  • 2022-10-12
  • 1970-01-01
  • 2013-03-06
  • 1970-01-01
  • 2011-03-09
  • 1970-01-01
  • 2021-10-07
  • 2021-12-20
相关资源
最近更新 更多