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

前端 未结 3 394
醉话见心
醉话见心 2020-12-05 14:28

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:



        
相关标签:
3条回答
  • 2020-12-05 14:54

    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.

    0 讨论(0)
  • 2020-12-05 15:03

    Partial answer: use typename = typename enable_if<...>, T=0 with different Ts:

    #include <iostream>
    #include <type_traits>
    
    class Class {
    public:
        template <
            typename U,
            typename = typename std::enable_if_t<
                std::is_const<typename std::remove_reference<U>::type>::value
            >, int = 0
        >
        void test() {
            std::cout << "Constant" << std::endl;
        }
    
        template <
            typename U,
            typename = typename  std::enable_if_t<
                !std::is_const<typename std::remove_reference<U>::type>::value
            >, char = 0
        >
        void test() {
            std::cout << "Mutable" << std::endl;
        }
    };
    
    int main() {
        Class c;
        c.test<int &>();
        c.test<int const &>();
        return 0;
    }
    

    (demo)

    Still trying to figure out what the heck does std::enable_if<...>::type... mean knowing the default type is void.

    0 讨论(0)
  • 2020-12-05 15:14

    I am not a language lawyer, but cannot the following quote be somehow connected to the problem?

    [temp.deduct.type/9]: If Pi is a pack expansion, then the pattern of Pi is compared with each remaining argument in the template argument list of A. Each comparison deduces template arguments for subsequent positions in the template parameter packs expanded by Pi.

    It seems to me that since there is no remaining argument in the template argument list, then there no comparison of the pattern (which contains enable_if). If there is no comparison, then there is also no deduction and substitution occurs after deduction I believe. Consequently, if there is no substitution, no SFINAE is applied.

    Please correct me if I am wrong. I am not sure whether this particular paragraph applies here, but there are more similar rules regarding pack expansion in [temp.deduct]. Also, this discussion can help someone more experienced to resolve the whole issue: https://groups.google.com/a/isocpp.org/forum/#!topic/std-discussion/JwZiV2rrX1A.

    0 讨论(0)
提交回复
热议问题