noexcept + declval无法在MSVC下编译

问题描述 投票:0回答:2

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

#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<type>().something())上-MSVC拒绝理解这一行。

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

2)我如何仍然可以对其进行编译?

c++ templates visual-c++ compiler-errors sfinae
2个回答
0
投票

您可以使用初始化为nullptr的指针。像这样

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

0
投票

MSVC本身的语法没有问题。似乎只是假设SFINAE在这种情况下不适用,因此将void替换为类型会产生错误。目前尚不知道标准中为什么或是否对此提供任何支持,但是可以通过将noexcept条件从声明移到struct的定义中来轻松解决,如果enable_if失败(即.cancel()根本不存在),将永远不会被替换:

template<class T>
struct has_cancel<T,
    typename std::void_t<decltype(std::declval<T>().cancel())    >
> : std::bool_constant<noexcept(std::declval<T>().cancel())> {};
© www.soinside.com 2019 - 2024. All rights reserved.