Force a compile time error in a template specialization
Asked Answered
O

2

7

I recently started to learn modern template metaprogramming and I wrote myself an index_of function for types.

template<std::size_t Index, class C, class A> struct mp_index_of_impl{};
template<std::size_t Index, class C,template<class...> class A,class ...Ts>
struct mp_index_of_impl<Index,C,A<C,Ts...>>{
    using type = std::integral_constant<std::size_t,Index>;
};
template<std::size_t Index, class C,template<class...> class A,class ...Ts,class T1>
struct mp_index_of_impl<Index,C,A<T1,Ts...>>{
    using type = typename mp_index_of_impl<Index + 1,C,A<Ts...>>::type;
};
template<std::size_t Index, class C,template<class...> class A> struct mp_index_of_impl<Index,C,A<>>{
    //static_assert(false,"Does not contain type");
    using type = std::integral_constant<std::size_t,0>;
};

The problem is my last specialization

template<std::size_t Index, class C,template<class...> class A> 
struct mp_index_of_impl<Index,C,A<>>{
      //throw a compile error here
};

I tried to use static_assert like this

template<std::size_t Index, class C,template<class...> class A> struct mp_index_of_impl<Index,C,A<>>{
    static_assert(false,"Does not contain type");
};

But this will throw a compile error every time, even if it is not matched.

I want to throw a compile error with a custom error message if this template specialization is matched. How would I do this?

Oyster answered 29/10, 2015 at 12:14 Comment(2)
static_assertDex
@101010 I have tried that, but it throws every time.Oyster
H
14

If you put a static_assert(false,...) in a template specialization then it is always impossible to generate valid code from it. This is ill-formed, no diagnostic required.

A workaround to this is to make your static_assert depend on the template parameter:

template<typename T>
struct assert_false : std::false_type
{ };

template <typename T>
inline T getValue(AnObject&)
{
    static_assert( assert_false<T>::value , "Does not contain type");
}
Hienhieracosphinx answered 29/10, 2015 at 12:20 Comment(5)
Thanks this was actually my seconds attempt, which works. static_assert(Index == 0,"Does not contain type");Oyster
@Barry really? But someone could come along and specialize assert_false for some T to inherit from std::true_type.Hienhieracosphinx
@Hienhieracosphinx Until someone does, it is. Which presumably you don't want them to do.Zetta
@Zetta But you don't know if assert_false has been specialized for some T until you try to instantiate it. Which standards passage are you referencing?Hienhieracosphinx
@Hienhieracosphinx There are only a handful of instances of that phrase in the standard "ill-formed, no diagnostic required" (I usually search for "no diagnostic required").Ivaivah
Z
3

To get a truly well-formed solution here is a little annoying. You're running into [temp.res]/8:

If no valid specialization can be generated for a template, and that template is not instantiated, the template is ill-formed, no diagnostic required... If a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter, the program is ill-formed; no diagnostic is required.

So what we need to do is avoid anything that could do static_assert(false, ...). In this case, we have a partial opening. We could specialize on the case where we only have one type left and assert that this is what we're looking for:

template<std::size_t Index, class C,template<class...> class A, class Last>
struct mp_index_of_impl<Index,C,A<Last>>{
    static_assert(std::is_same<C, Last>::value,"Does not contain type");
    using type = std::integral_constant<std::size_t, Index>;
};

This would not handle the case of empty lists, but for that at top level you can add another static_assert for non-emptiness:

template<std::size_t Index, class C,template<class...> class A>
struct mp_index_of_impl<Index,C,A<>>{ };

template <typename T> is_empty : std::false_type { };
template <template <class...> class A> is_empty<A<>> : std::true_type { };

template <class C, class X>
struct mp_index_of
: mp_index_of_impl<0, C, X>
{
    static_assert(!is_empty<X>::value, "Does not contain type");
};

Although, in practice, TartanLlama's solution will probably get you by every compiler, and I would probably just use it.

Zetta answered 29/10, 2015 at 12:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.