Using alias templates for sfinae: does the language allow it?

后端 未结 2 931
梦毁少年i
梦毁少年i 2021-02-01 04:02

I have just discovered the following technique. It looks very close to one of proposed concepts syntax, works perfectly on Clang, GCC and MSVC.

template 

        
相关标签:
2条回答
  • 2021-02-01 04:42

    It works and allowed because it relays on widely used C++ features allowed by the standard:

    1. SFINAE in function parameters ([temp.over]/1, [temp.deduct]/6, [temp.deduct]/8):

      template <typename T>
      void foo(T&& v, typename std::enable_if<std::is_rvalue_reference<T&&>::value>::type* = nullptr)
      { /* ... */ }
      

      we cannot deduce on the actual parameter like void foo(typename std::enable_if<std::is_rvalue_reference<T&&>::value, T>::type&&) (CWG#549), but it is possible to workaround this limitation with template aliases (it is the trick I have presented in my question)

    2. SFINAE in template parameter declaration ([temp.deduct]/7):

      template <typename T, typename std::enable_if<std::is_rvalue_reference<T&&>::value>::type* = nullptr>
      void foo(T&& v)
      { /* ... */ }
      
    3. Alias templates in function parameters ([temp.alias]/2):

      template<class T> struct Alloc { /* ... */ };
      template<class T> using Vec = vector<T, Alloc<T>>;
      
      template<class T>
        void process(Vec<T>& v)
        { /* ... */ }
      
    4. Alias templates can have default parameters ([temp.param]/12, [temp.param]/15, [temp.param]/18)

    5. Template parameters of alias templates parameterized with deducible types still can be deduced ([temp.deduct.type]/17):

    I have accepted @Barry's answer and put this one (with concentrated info and about every aspect the trick uses) because a lot of people (including me) are scared of C++ standard voodoo language about template deduction stuff.

    0 讨论(0)
  • 2021-02-01 04:45

    [...] does the language actually allows it?

    Can't say anything about the name, but this seems to me to be a yes.

    The relevant wording is [temp.alias]/2:

    When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template.

    and the sfinae rule, [temp.deduct]/8:

    Only invalid types and expressions in the immediate context of the function type, its template parameter types, and its explicit-specifier can result in a deduction failure.

    Taking an argument of type require_rvalue<T> does behave as if we substitute that alias, which either gives us a T&& or a substitution failure - and that substitution failure is arguably in the immediate context of the substitution and so is "sfinae-friendly" as opposed to being a hard error. Note that even though the defaulted type argument is unused, as a result of CWG 1558 (the void_t rule), we got the addition of [temp.alias]/3:

    However, if the template-id is dependent, subsequent template argument substitution still applies to the template-id.

    This ensures that we still substitute into the defaulted type argument to trigger the required substitution failure.

    The second unsaid part of the question is whether this actually can behave as a forwarding reference. The rule there is in [temp.deduct.call]/3:

    A forwarding reference is an rvalue reference to a cv-unqualified template parameter that does not represent a template parameter of a class template (during class template argument deduction ([over.match.class.deduct])). If P is a forwarding reference and the argument is an lvalue, the type “lvalue reference to A” is used in place of A for type deduction.

    Is an alias template with one template parameter whose associated type is an rvalue reference to its cv-unqualified template parameter considered a forwarding reference? Well, [temp.alias]/2 says that require_rvalue<T> is equivalent to T&&, and T&& is the right thing. So arguably... yeah.

    And all the compilers treat it as such, which is certainly a nice validation to have.


    Although, note the existence of CWG 1844 and the lack of actual definition for immediate context, and the example there which also relies upon a substitution failure from a defaulted argument - which the issue states has implementation divergence.

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