Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the void_t<> detection idiom not work with gcc-4.9? [duplicate]

Consider the following code:

#include <iostream>
#include <type_traits>

struct Test { Test& operator++(); };
struct NoIncrement { };

template <typename...> using void_t = void;

template <class, class=void_t<>>
struct has_pre_increment_member : std::false_type { };

template <class T>
struct has_pre_increment_member<T, void_t<decltype( ++std::declval<T&>() )>>
  : public std::true_type { };

int main() {
  std::cout << has_pre_increment_member<Test>::value << " ";
  std::cout << has_pre_increment_member<NoIncrement>::value << std::endl;
}

With g++ version 5 and later (and the -std=c++14 flag, of course), this code outputs

1 0

as it should. With g++ version 4.9 (and the -std=c++14 flag), however, it outputs

1 1

Both claim to be using the same language standard, so what's the issue here?

like image 915
Daisy Sophia Hollman Avatar asked Mar 02 '16 17:03

Daisy Sophia Hollman


1 Answers

This comes about as a result of CWG Issue 1558, and is now considered a bug in gcc (specifically 64395 - currently fixed). The idea behind the issue is that since you don't actually use the template parameters here:

template <typename...> using void_t = void;

there's no substitution failure regardless of what types or expressions you try to pass in.

Thankfully, there's an easy workaround that doesn't involve upgrading your compiler. We can rewrite void_t to actually use its parameter pack, thereby triggering the substitution failure:

namespace void_details {
    template <class... >
    struct make_void { using type = void; };
}

template <class... T> using void_t = typename void_details ::make_void<T...>::type;

That'll make your example do the right thing across all the gcc versions I tried.

like image 188
Barry Avatar answered Nov 05 '22 17:11

Barry