noexcept + declval не может скомпилироваться под MSVC - PullRequest
1 голос
/ 27 января 2020

Я пытаюсь реализовать ответ, который я получил на свой вопрос SO здесь : Моя цель - обнаружить существование метода 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()) - MSV C отказывается чтобы понять эту линию.

1) почему MSV C жалуется? это ошибка в коде или в компиляторе?

2) как мне все же заставить его скомпилироваться?

1 Ответ

2 голосов
/ 27 января 2020

MSV C не имеет проблемы с самим синтаксисом. Просто кажется, что 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())> {};
...