Search code examples
c++templatesc++20template-argument-deduction

Deducing a user-defined-value template argument (C++2a, P0732R2)


I am trying to get the value of a template parameter of a user-defined class deduced (http://wg21.link/p0732r2), using GCC 9.1 with -std=c++2a.

struct user_type {
   int a;
   constexpr user_type( int a ): a( a ){}
};

template< user_type u > struct value {};

template< user_type u > void f( value< u > arg ){}  

void g(){
  f( value< user_type( 0 ) >() ); // error here
}

compiler explorer: https://godbolt.org/z/6v_p_R

I get the error:

source>:8:30: note:   template argument deduction/substitution failed:
<source>:11:33: note:   couldn't deduce template parameter 'u'
   11 |    f( value< user_type( 0 ) >() );

Am I doing something wrong? I had expected such a value to be deductible.

As suggested by Nikita I added == and != operators to user-type, but that made no difference.

struct user_type {
   int a;
   constexpr user_type( int a ): a( a ){}
   constexpr bool operator==( const user_type & arg ) const {
      return a == arg.a;
   }
   constexpr bool operator!=( const user_type & arg ) const {
      return a != arg.a;
   }
};

Solution

  • This should be ill-formed:

    struct user_type {
       int a;
       constexpr user_type( int a ): a( a ){}
    };
    
    template< user_type u > struct value {};
    

    In order to be a template non-type parameter, you need to satisfy [temp.param]/4:

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

    • a literal type that has strong structural equality ([class.compare.default]),
    • [...]

    Where strong structural equality requires, from [class.compare.default]/3:

    A type C has strong structural equality if, given a glvalue x of type const C, either:

    • C is a non-class type and [...], or
    • C is a class type with an == operator defined as defaulted in the definition of C, x == x is well-formed when contextually converted to bool, all of C's base class subobjects and non-static data members have strong structural equality, and C has no mutable or volatile subobjects.

    The key is that we need a defaulted == in the type... and we don't have one, so our type doesn't have strong structural equality, so it cannot be used as a template non-type parameter.

    However, gcc doesn't let you declare such an operator yet, so you can't fix the problem.

    This is just an incomplete implementation of a new feature.