Consider the following, basic example based on void_t
:
template>
struct S: std::false_type {};
template&l
I used both void_t and trailing decltype when I was implementing my own homebrew version of Concepts Lite (I succeded, by the way), which required creation of many additional type traits, most of which use Detection idiom in one way or another. I used void_t, trailing decltype and preceding decltype.
As far as I understood, these options are logically equivalent, so an ideal, 100%-conforming compiler should produce the same result using all of them. The problem, however, is that a particular compiler may (and will) follow different patterns of instantiation in different cases, and some of these patterns can go way beyond internal compiler limits. For example, when I tried to make MSVC 2015 Update 2 3 detect presence of multiplication by the same type, the only solution that worked was preceding decltype:
template
struct has_multiplication
{
static no_value test_mul(...);
template
static decltype(*(U*)(0) *= std::declval() * std::declval()) test_mul(const U&);
static constexpr bool value = !std::is_same())) >::value;
};
Every other version produced internal compiler errors, though some of them worked fine with Clang and GCC. I also had to use *(U*)(0)
instead of declval
, because using three declval
's in a row, though perfectly legal, was just to much for the compiler in this particular case.
My bad, I forgot. Actually I used *(U*)(0)
because declval
produces rvalue-ref of type, which can't be assigned to, and that's why I used this. But everything else is still valid, this version worked where others didn't.
So for now my answer would be: "they're identical, as long as your compiler thinks they are". And this is something you have to find out by testing. I hope that this will stop being an issue in the following releases of MSVC and others.