Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

clang/gcc inconsistency in class specialization

I came across this issue while trying to specialize tuple_size/tuple_element for a custom class in C++17 for structured binding.

Below code compiles in GCC, but not in clang (both trunk versions, see below link).

#include <type_traits>

template<typename T, typename... Ts>
using sfinae_t = T;

template<typename T, bool... Bs>
using sfinae_v_t = sfinae_t<T, typename std::enable_if<Bs>::type...>;

template <typename T>
struct Test;

template <typename T>
struct Test<sfinae_v_t<T, std::is_integral_v<T>>> {};

void f() {
    Test<int> t;
}

https://godbolt.org/z/ztuRSq

This is the error provided by clang:

<source>:13:8: error: class template partial specialization does not specialize any template argument; to define the primary template, remove the template argument list

struct Test<sfinae_v_t<T, std::is_integral<T>::value>> {};

       ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

1 error generated.

Compiler returned: 1

Is this is a bug in either compiler or does above code invokes some UB?

like image 818
ofo Avatar asked Jan 15 '20 16:01

ofo


1 Answers

What I tell below (under OLD POST) should be true to a degree, but the actual problem with this is, that SFINAE is used wrongly, hence I am not that sure anymore that this is a bug in gcc.

An alias declaration must always succeed, you cannot SFINAE there, since it is not a class or function declaration or specializations (that makes sense, since you cannot specialize aliases). If the alias declaration does not succeed, the programm is ill-formed. Hence the compiler may assume that it will never come to the case that the alias declaration does not succeed until you force it to instantiate such a template.

Hence it is perfectly acceptable for the compiler to think that sfinae_v_t<T,...> is always T, since that will happen, when the programm is not ill-formed. Hence it will see, that in all cases in which the programm is not ill-formed, the partial specialization does not specialize and as such it will tell you that this is ill-formed. (That is what clang does).

I don't think that the compiler is forced to do this. And if it does not, and just thinks "Ok, sfinae_v_t is some type, whatever.", then it is not obvious that this a redeclaration. So I think until we instantiate one of them there is nothing wrong with not throwing an error.

But when we instantiate it there should be either the problem that we have a redeclaration or that the program is ill-formed due to std::enable_if, depending on the template argument. GCC should pick up at least one of them but does neither.

This also does absolutely not apply to the more easy example without std::enable_if. So I still think this is a bug in GCC, but I am sufficiently mindfucked that I cannot say that with certainity anymore. I would just say, someone should report that as a bug and let the people from gcc think about it.

OLD POST

This is a bug in gcc. The standard gives us rules for converting a class template in function templates. One class template is more specialized than another if its function comes before the other's in the partial function template ordering.

I created the functions here and now gcc claims that calling them is ambiguous, hence it would also have to say that the class templates are equally specified.

Note: Reading the standard carefully, the compiler in my head agrees with clang.

like image 103
n314159 Avatar answered Nov 15 '22 11:11

n314159