Why void_t doesnt work in SFINAE but enable_if does
Asked Answered
D

1

21

I was trying to understand how SFINAE works and I was experimenting with this code

#include <type_traits>

struct One { 
  using x = int; 
};
struct Two { 
  using y = int; 
};

template <typename T, std::void_t<typename T::x>* = nullptr>
void func() {}
template <typename T, std::void_t<typename T::y>* = nullptr>
void func() {}

/*template <typename T, std::enable_if_t<std::is_same_v<typename T::x, typename T::x>>* = nullptr>
void func() {}
template <typename T, std::enable_if_t<std::is_same_v<typename T::y, typename T::y>>* = nullptr>
void func() {} */



int main() {
  func<One>();
  func<Two>();
}

The commented code works but the first doesn't. The compiler gives me errors saying that there is a redefinition and that template argument deduction failed. Could someone explain why this happens? The two void_ts should be independent right? Since one line checks for x and the other for y. How can I fix?

Darill answered 30/6, 2017 at 11:57 Comment(1)
Note that this works with the make_void trick.Teofilateosinte
T
16

This seems to be related to CWG issue #1980 (credits to T.C. for correcting me).

As a workaround you can define void_t as:

template<typename... Ts> struct make_void { typedef void type;};
template<typename... Ts> using void_t = typename make_void<Ts...>::type;

(from cppreference)

live example on wandbox

Teofilateosinte answered 30/6, 2017 at 12:14 Comment(12)
Whoa this problem has still not been fixed in the new installations of C++17 on gcc?Darill
Apparently not. :( I do see a difference on g++ 7.1.0 between the standard void_t and the above void_t.Rudolfrudolfo
@Whoami: I am surprised as well. If my answer is indeed correct, this is quite sadTeofilateosinte
@VittorioRomeo they could have at least used the make_void trick in the library..Darill
@VittorioRomeo sorry but could you confirm to me once that they have not used the make_void trick in the library? I am not sure where to checkDarill
@Whoami: the sources for libstdc++ and libc++ are publicly available, there are some mirrors on GitHub.Teofilateosinte
@Whoami: added links in my answer.Teofilateosinte
std::void_t is specified as template <class...> using void_t = void;Dryclean
Yeah, those definitely aren't compiler bugs. LEWG issue?Wivestad
This is not 1558. This has to do with declaration matching, i.e., whether the two func definitions actually declare distinct function templates or the same one. Closer to 2037.Statolith
@T.C.: interesting, all the hints seemed to point to 1558 and the make_void workaround. I tried my best, apologies for the possible misinformation. Would like to know why this is not related to 1558 though.Teofilateosinte
"all the hints seemed to point to 1558"? Not to me. First, you don't get anything like "ambiguous overload", which would be an indication that SFINAE's not kicking in; instead, you get "redefinition". Second, if you remove one of the two funcs, then the remaining one gets called - or yield an error - appropriately. That's also not consistent with a SFINAE problem. Finally, just having the two definitions of func, without any calling, is sufficient to trigger the error. Once again, that's not consistent with a SFINAE problem.Statolith

© 2022 - 2024 — McMap. All rights reserved.