1

I'm thought I understood how a static_assert worked. But when I tried this on a g++ compiler, I started to wonder:

#include <iostream>
#include <type_traits>

#define ENABLE_IF(...) std::enable_if_t<__VA_ARGS__, int> = 0

template<typename...Ts>
struct list{};

template<typename...Ts>
struct is_one_of;

template<template <typename...> class TT, typename T, typename T1, typename...Ts>
struct is_one_of<T, TT<T1, Ts...>> : is_one_of<T, TT<Ts...>> {};

template<template <typename...> class TT, typename T, typename...Ts>
struct is_one_of<T, TT<T, Ts...>> : std::true_type {};

template<template <typename...> class TT, typename T>
struct is_one_of<T, TT<>> : std::false_type {};


template<typename...Ts>
struct X;

template<typename P, typename T, typename...Ts>
struct X<P, T, Ts...> : X<P, Ts...>
{
  using X<P, Ts...>::fn;

  template<typename R, ENABLE_IF(std::is_same<T, R>::value)>
  constexpr auto fn(R&& x)
  {
    return x;
  }
};

template<template <typename...> class TT, typename...Ts>
struct X<TT<Ts...>>
{
  template<typename R, ENABLE_IF(!is_one_of<R, TT<Ts...>>::value)>
  constexpr auto fn(R&& x)
  {
    static_assert(false, "Type R didn't match");
  }
};


template<typename...Ts>
struct XX : X<list<Ts...>, Ts...> {};


int main() {
    XX<int, float> x;
    std::cout << x.fn(int(3)) << std::endl;
    return 0;
}

Now I would have thought that there wouldn't be any way that the base type X<TT<Ts...>> could have ever been instantiated because it is never called. And through this reasoning, it should not cause a static_assert failure.

This fails on g++ (5.4.0) and clang (3.9.1) but worked on VC++ 2015.

Is this a defect or am I missing something?

Barry
  • 286,269
  • 29
  • 621
  • 977
Adrian
  • 10,246
  • 4
  • 44
  • 110

1 Answers1

4

Writing static_assert(false) is simply ill-formed, because it runs afoul of [temp.res]:

The program is ill-formed, no diagnostic required, if:
— no valid specialization can be generated for a template or a substatement of a constexpr if statement (6.4.1) within a template and the template is not instantiated

You can think of it as - since the static_assert's constant expression isn't dependent, the compiler can just immediately see that it's ill-formed and fire.


You can either just not provide a definition of fn() for that specialization, or you could do something like:

template <class T> struct always_false : std::false_type { };
static_assert(always_false<some_dependent_type>::value, "...");

This would make it hypothetically possible for a valid specialization to be generated, even if nobody should ever specialize always_false.

Barry
  • 286,269
  • 29
  • 621
  • 977