9

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?

Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141
ofo
  • 1,160
  • 10
  • 21

1 Answers1

3

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.

n314159
  • 4,990
  • 1
  • 5
  • 20
  • Are `sfinae_v_t>` and `sfinae_v_t>` treated as same types? Semantically, they aren't. – ofo Jan 15 '20 at 17:39
  • @GuillaumeRacicot Quite possibly, but I'd like to understand why exactly. For example the standard also [says](http://eel.is/c++draft/temp.class.spec#10) "Dependent names cannot be checked when declaring the partial specialization, but will be checked when substituting into the partial specialization." Doesn't that mean whether they are same type is to be decided after substituting T in partial specialization since `sfinae_v_t` is dependent on `T`? In which case, they wouldn't be same because either one will be ill-formed. – ofo Jan 15 '20 at 18:04
  • @ofo I have to say, I am not sure. It is a bit of a mindfuck to even think about those two since one of them will never be a type and using them both in a non-template context will result in a compile error due to the `enable_if_t`. My best read of the standard is, that it does not matter if they are the same or not. For the partial ordering we will always compare the templare parameter form of one function to the template argument form of the other (i.e. the `int` is already substituted) and then there is a realy type in one of them, so we don't have to compare them abstractly. – n314159 Jan 15 '20 at 18:29
  • 1
    Digging deeper, I found [this](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1554) from [here](https://stackoverflow.com/a/13730889/7771076). SFINAE should work fine with template aliases, otherwise `template enable_if_t = typename enable_if::type;` wouldn't work either. I will go ahead and file the bug against gcc, but not really sure if gcc is wrong there. Thanks. – ofo Jan 15 '20 at 21:00