Is There a Compiler Bug Exposed by My Implementation of an Is_Complete Type Trait

Is there a compiler bug exposed by my implementation of an is_complete type trait?

The problem appears to be with the definition of void_t. Defining it as

template<typename... Ts>
struct make_void { typedef void type;};

template<typename... Ts>
using void_t = typename make_void<Ts...>::type;

instead yields the correct result (10) on both compilers (Demo).

I believe this is the same issue noted in section 2.3 of N3911, the paper proposing void_t, and CWG issue 1558. Essentially, the standard was unclear whether unused arguments in alias template specializations could result in substitution failure or are simply ignored. The resolution of the CWG issue, adopted at the Committee's November 2014 meeting, clarifies that the shorter definition of void_t in the question should work, and GCC 5.0 implements the resolution.

C++11: SFINAE in template parameters, GCC vs Clang

This was CWG issue 1558. The unspecified part was treatment of unused arguments in an alias template. In GCC < 5.0 the unused arguments can't result in a substitution failure, hence void_t fails to verify your functor call, and tries to instantiate a class template specialization, which results in a hard error.

A workaround for GCC (< 5.0) is the following implementation:

template <typename...>
struct voider
{
using type = void;
};

template <typename... Ts>
using void_t = typename voider<Ts...>::type;

DEMO

Checking if non-member function that accepts T param exists

The discrepancy between the compilers is caused by the definition of void_t:
Is there a compiler bug exposed by my implementation of an is_complete type trait?
In short, the standard was unclear whether unused arguments in alias template specializations could result in substitution failure or are simply ignored. The resolution to CWG issue 1558 clarifies that the shorter definition of void_t in the question should work.

With that issue worked around using

template<typename... Ts>
struct make_void { typedef void type;};

template<typename... Ts>
using void_t = typename make_void<Ts...>::type;

both compilers produce 10.

§14.6.4.2 [temp.dep.candidate]:

For a function call that depends on a template parameter, the
candidate functions are found using the usual lookup rules (3.4.1,
3.4.2, 3.4.3) except that:

  • For the part of the lookup using unqualified name lookup (3.4.1) or qualified name lookup (3.4.3), only function declarations from the
    template definition context are found.
  • For the part of the lookup using associated namespaces (3.4.2), only function declarations found in either the template definition context
    or the template instantiation context are found.

If the function name is an unqualified-id and the call would be
ill-formed or would find a better match had the lookup within the
associated namespaces considered all the function declarations with
external linkage introduced in those namespaces in all translation
units, not just considering those declarations found in the template
definition and template instantiation contexts, then the program has
undefined behavior.

Unqualified lookup for Serialize is performed in template definition context and will not find Serialize(int &), and there's no ADL for an argument of type int&, so 10 is the correct output.

Selecting traits with enable_if - works with clang, but not with gcc

Looks like it's yet another manifestation of CWG issue 1558. The standard was unclear whether unused template arguments in alias templates can result in substitution failure. Clang treats it as substitution failure; GCC simply ignores the unused arguments.

Use

template<typename... Ts>
struct make_void { typedef void type;};

template<typename... Ts>
using ToVoid = typename make_void<Ts...>::type;

instead and you should see it compile in both compilers.

Detect operator at compile time without implicit conversions

Well, you don't have to use void_t (which is syntax sugar, anyway). Bog-standard expression SFINAE is supported by GCC 4.9:

template <typename, typename = void>
struct has_ostream_operator : std::false_type {};

template <typename T>
struct has_ostream_operator<T, decltype(void(std::declval<std::ostream&>() << std::declval<const T&>()))>
: std::true_type {};

works fine on Wandbox's GCC 4.9.

How to access a possibly unexisting type-alias in C++11?

template <typename...>
struct voider { using type = void; };

template <typename... Ts>
using void_t = typename voider<Ts...>::type;

template <typename, typename = void_t<>>
struct MyTypeOrTupple { using type = std::tuple<>; };

template <typename T>
struct MyTypeOrTupple<T, void_t<typename T::MyType>> { using type = typename T::MyType; };

template <typename T>
using MyTypeOrTupple_t = typename MyTypeOrTupple<T>::type;

DEMO

What does SFINAE not work correctly with following has_member function?

The problem is that gcc 4.8.2 (and prior to gcc 5.0) does not regard unused arguments in alias templates as suitable for SFINAE. The workaround is to forward to a voider class template:

template <class ... T> struct voider { using type = void; };
template <class ... T>
using void_t = typename voider<T...>::type;

From http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3911.pdf section 2.3:

Alas, we have encountered implementation divergence (Clang vs. GCC) while working with the
above very simple definition. We (continue to) conjecture that this is because of CWG issue 1558:
“The treatment of unused arguments in an alias template specialization is not specified by the
current wording of 14.5.7 [temp.alias].”

Has template member - template template deduction

template<template<class...> class...>
using void_templ = void;

template <typename, typename = void>
struct has_test_impl {
using type = std::false_type;
};
template <typename T>
struct has_test_impl<T, void_templ<T::template test>> {
using type = std::true_type;
};


Related Topics



Leave a reply



Submit