Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
150 views
in Technique[技术] by (71.8m points)

c++ - Why do my SFINAE expressions no longer work with GCC 8.2?

I recently upgraded GCC to 8.2, and most of my SFINAE expressions have stopped working.

The following is somewhat simplified, but demonstrates the problem:

#include <iostream>
#include <type_traits>

class Class {
public:
    template <
        typename U,
        typename std::enable_if<
            std::is_const<typename std::remove_reference<U>::type>::value, int
        >::type...
    >
    void test() {
        std::cout << "Constant" << std::endl;
    }

    template <
        typename U,
        typename std::enable_if<
            !std::is_const<typename std::remove_reference<U>::type>::value, int
        >::type...
    >
    void test() {
        std::cout << "Mutable" << std::endl;
    }
};

int main() {
    Class c;
    c.test<int &>();
    c.test<int const &>();
    return 0;
}

C++ (gcc) – Try It Online

C++ (clang) – Try It Online

Older versions of GCC (unfortunately I don't remember the exact version I had installed previously) as well as Clang compile the above code just fine, but GCC 8.2 gives an error stating:

 : In function 'int main()':
:29:19: error: call of overloaded 'test()' is ambiguous
     c.test();
                   ^
:12:10: note: candidate: 'void Class::test() [with U = int&; typename std::enable_if::type>::value>::type ... = {}]'
     void test() {
          ^~~~
:22:10: note: candidate: 'void Class::test() [with U = int&; typename std::enable_if::type>::value)>::type ... = {}]'
     void test() {
          ^~~~
:30:25: error: call of overloaded 'test()' is ambiguous
     c.test();
                         ^
:12:10: note: candidate: 'void Class::test() [with U = const int&; typename std::enable_if::type>::value>::type ... = {}]'
     void test() {
          ^~~~
:22:10: note: candidate: 'void Class::test() [with U = const int&; typename std::enable_if::type>::value)>::type ... = {}]'
     void test() {

As is usually the case when different compilers and compiler versions handle the same code differently I assume I am invoking undefined behavior. What does the standard have to say about the above code? What am I doing wrong?


Note: The question is not for ways to fix this, there are several that come to mind. The question is why this doesn't work with GCC 8 - is it undefined by the standard, or is it a compiler bug?

Note 2: Since everyone was jumping on the default void type of std::enable_if, I've changed the question to use int instead. The problem remains.

Note 3: GCC bug report created

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

This is my take on it. In short, clang is right and gcc has a regression.

We have according to [temp.deduct]p7:

The substitution occurs in all types and expressions that are used in the function type and in template parameter declarations. [...]

This means that the substitution has to happen whether or not the pack is empty or not. Because we are still in the immediate context, this is SFINAE-able.

Next we have that a variadic parameter is indeed considered an actual template parameter; from [temp.variadic]p1

A template parameter pack is a template parameter that accepts zero or more template arguments.

and [temp.param]p2 says which non-type template parameters are allowed:

A non-type template-parameter shall have one of the following (optionally cv-qualified) types:

  • a type that is literal, has strong structural equality ([class.compare.default]), has no mutable or volatile subobjects, and in which if there is a defaulted member operator<=>, then it is declared public,

  • an lvalue reference type,

  • a type that contains a placeholder type ([dcl.spec.auto]), or

  • a placeholder for a deduced class type ([dcl.type.class.deduct]).

Note that void doesn't fit the bill, your code (as posted) is ill-formed.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...