I implemented std::experimental::is_detected
based on this article on cppreference.com (Part of the code is below + working repro).
It works well on G++ and Clang++, but results in strange errornous behaviour with MSVC: is_detected
seems to always be bool_constant<true>
!
Here you can see the correct result using gcc 5.x
:
@ideone.com
But with MSVC 19 (shipped with VS2015) the tests always succeed:
Z:\>cl /EHsc test.cxx
....
Z:\>test
true, true
So, is this a known bug in the compiler? Is it related to Expression SFINAE not being implemented correctly? Is there any workaround I can use to make this work?
Thank you!
Here is part of the code that reproduces the error (I omitted the rest of the interface besides is_detected
to increase legibility):
#include <iostream>
// void_t: void type alias
template< typename... >
using void_t = void;
//
namespace internal
{
// Fallback case
template< typename D,
typename Void,
template< typename... > class Check,
typename... Args
>
struct detect_impl
{
using value_t = std::false_type;
using type = D;
};
// Check succeeded
template< typename D,
template< typename... > class Check,
typename... Args
>
struct detect_impl
< D, void_t< Check<Args...> >, Check, Args... >
{
using value_t = std::true_type;
using type = Check<Args...>;
};
}
// Type representing a missing type.
struct nonesuch
{
nonesuch() = delete;
~nonesuch() = delete;
nonesuch(nonesuch const&) = delete;
void operator=(nonesuch const&) = delete;
};
template< template< typename... > class Check,
typename... Args
>
using is_detected = typename internal::detect_impl< nonesuch, void, Check, Args... >::value_t;
// Our test
template< typename T >
using is_addable_impl = decltype( std::declval<T>() + std::declval<T>() );
template< typename T >
using is_addable = is_detected<is_addable_impl, T>;
auto main(int argc, const char* arv[])
-> int
{
std::cout << std::boolalpha
<< is_addable<int>::value << ", "
<< is_addable<nonesuch>::value << std::endl;
}
EDIT: Strangely, directly using the void_t idiom works on MSVC:
#include <iostream>
#include <type_traits>
struct X {};
template< typename T, typename = void >
struct is_addable
: std::false_type
{};
template< typename T >
struct is_addable <T, std::void_t<decltype(std::declval<T>() + std::declval<T>())>>
: std::true_type
{};
int main()
{
std::cout << std::boolalpha
<< is_addable<int>::value << ", "
<< is_addable<X>::value
<< std::endl;
}
Output:
Z:\>cl /EHsc test.cxx
....
Z:\>test.exe
true, false
Here's a workaround that appears to work with recent MSVC (tested with Visual C++ 19.00.23720.0):
(The dummy
void
parameter is unused now, it's there just to keep the rest of the implementation intact.)You need a C++11 compiler to compile the above code, and MSVC 2015 is not a C++11 compiler.
The particular deficit in C++11 compliance you are running into is called "expression SFINAE" by microsoft. Keep an eye out for it being fixed.
Basically,
decltype
cannot be used for SFINAE. In laymans terms, SFINAE is the technique you are using for selecting overloads of template functions or classes.There is usually no workaround.