Reputation: 1387
I'm trying to expand a pack of variadic types to another type by doing something like this.
I have a simple trait that gets enabled when TFunction
returns TReturn
.
template<typename TReturn, typename TFunction>
using ReturnsType = enable_if_t<is_same<decltype(declval<TFunction>()()),
TReturn>::value>;
it works perfectly in all main compilers (VC++, GCC, CLANG).
Now i want to create another one with this notation:
template<typename TReturn, typename... TFunctions>
using AllReturnsType = ???
so it gets enabled only if all the passed functions (TFunctions...
) do return TReturn
.
So I tried this implementation:
template<typename TReturn, typename... TFunctions>
using AllReturnsType = void_t<ReturnsType<TReturn, TFunctions>...>;
but it fails in VC++ (it's fine in GCC and CLANG)
code playground can be found here: https://godbolt.org/g/d839wp
Is this a bug in MSVC++? how can I make it work in all compilers?
Thanks!
Edit: I'm in contact with MSVC team members and they are taking care of it! It is a confirmed bug.
Upvotes: 3
Views: 157
Reputation: 41100
I agree with max66 that it appears to be a MSVC bug. If I had to guess what was happening, I would say it probably comes down to the fact that MSVC still doesn't have full support for expression SFINAE.
It seems they've been making steady improvements, though, and their implementation of std::result_of is advertised to use it.
Coincidentally, what you're trying to achieve with get_return_type
:
template<typename Function>
using get_return_type = decltype(declval<Function>()());
Has already been done with std::result_of
(deprecated), and std::invoke_result
Since the latest MSVC appears to lack support for std::invoke_result
, it appears that if we modify your code to use std::result_of
instead, it all compiles just fine:
template<class Function>
using get_return_type = typename std::result_of<Function()>::type;
Upvotes: 2
Reputation: 66210
Is this a bug in MSVC++?
Yes, I think so.
how can I make it work in all compilers?
Don't ask me why but seems that, if you pass through an helper struct
for enable_if_returns
as follows
template <typename T, typename F,
bool = std::is_same_v<T, get_return_type<F>>>
struct enIfR
{ };
template <typename T, typename F>
struct enIfR<T, F, true>
{ using type = T; };
template <typename T, typename F>
using enable_if_returns = typename enIfR<T, F>::type;
your code compile also in MSVC.
The following is a full compiling (MSVC also) example
#include <iostream>
#include <type_traits>
template <typename Function>
using get_return_type = decltype(std::declval<Function>()());
template <typename T, typename F,
bool = std::is_same_v<T, get_return_type<F>>>
struct enIfR
{ };
template <typename T, typename F>
struct enIfR<T, F, true>
{ using type = T; };
template <typename T, typename F>
using enable_if_returns = typename enIfR<T, F>::type;
template<typename T, typename Function>
using enable_if_returns0 = typename std::enable_if<
std::is_same<T, get_return_type<Function>>::value>::type;
template<typename T, typename... Functions>
using enable_if_all_returns = std::void_t<typename enIfR<T, Functions>::type...>;
int main()
{
constexpr auto fint = []() -> int { return 1; };
constexpr auto fstring = []() -> std::string { return "asd"; };
(enable_if_returns<int, decltype(fint)>)0; // ok
//(enable_if_returns<int, decltype(fstring)>)0; // fail on all (expected)
//(enIfR<int, decltype(fstring)>::type)0; // fail on all (expected)
(enable_if_all_returns<int, decltype(fint)>)0; // fail in MSVC (NOT expected, 'get_return_type<unknown-type>' being compiled)
//(enable_if_all_returns<int, decltype(fint), decltype(string)>)0; // fail in all (expected)
return 0;
}
Upvotes: 2