C ++ operator overload and associated namespace

The following simplified example compiles in gcc and Visual Studio but fails in clang !?

 namespace N { struct A {}; template <typename T> double operator+ (T a, double d) {return d;} template <typename T> double operator+ (double d, T a) {return d;} } void test() { N::A a; double x; double y = a + x; double z = x + a; } 

As I can see, the templated operator+ in the namespace N must be found by ADL.

Why does clang not agree? Is this a bug in clang or in other compilers?

Here is a compilation error from clang 3.5.1 (tested on coliru), I don’t understand what the problem is here ...

 10 : error: overloaded 'operator+' must have at least one parameter of class or enumeration type double operator+ (double d, T a) {return d;} ^ 18 : note: in instantiation of function template specialization 'N::operator+' requested here double y = a + x; ^ 7 : error: overloaded 'operator+' must have at least one parameter of class or enumeration type double operator+ (T a, double d) {return d;} ^ 19 : note: in instantiation of function template specialization 'N::operator+' requested here double z = x + a; ^ 2 errors generated. Compilation failed 

Of course, the example is simplified from real-life code. It is assumed that any class defined inside the namespace N has an overloaded + operator with a double.

+5
source share
2 answers

This is caused by two different CWG issues: CWG issue 2052 and CWG release 1391 .

First, CWG 1391. When x + a encountered, x + a regular name lookup finds among other overloads

 template <typename T> double operator+ (T, double); 

The output of the template argument is done by matching T with the type lhs + , which is double , so it outputs T as double . The second type of parameter does not contain a template parameter, so it is not considered in accordance with current rules. Of course, N::A cannot be converted to double , so the resulting specialization is not viable, but current rules say that the output of the template argument does not care about that; which will be processed when overload resolution is enabled.

The proposed resolution to WG 1391, among other things, adds a new standard to the standard:

If the output is successful for all parameters that contain template parameters that are involved in the output of the template argument, and all template arguments are explicitly specified, outputted, or derived from the template arguments by default, the remaining parameters are then compared with the corresponding arguments. For each remaining parameter P with a type that did not depend on the replacement of any explicitly specified template arguments, if the corresponding argument A cannot be implicitly converted to P , output is impossible. [Note: parameters with dependent types in which there are no template parameters to participate in the output of the template argument and parameters that become independent of the replacement of the explicitly specified template, the arguments will be checked during overload resolution. -end note]

In other words, if the argument ( a in our case) corresponding to the independent parameter ( double ) cannot be converted to the parameter type, the output will simply fail. Therefore, in our case, the output of the template argument post-CWG1391 will fail for this overload, and everything will be fine.

Clang implements the current rules, so the output is completed using T = double , a replacement occurs, and we encounter CWG 2052. We quote the entry by Richard Smith (Clang developer):

In type example

  struct A { operator int(); }; template<typename T> T operator<<(T, int); void f(A a) { 1 << a; } 

The output of the template argument is appropriate for the operator template, creating the signature operator<<(int,int) . The resulting declaration is synthesized and added to the overload set in 14.8.3 [temp.over], clause 1. However, this violates requirement 13.5 [over.oper], clause 6,

An operator function must be either a non-static member function or a non-member function that has at least one parameter whose type is a class, class reference, enumeration, or enumeration reference.

This is not the SFINAE context, so the program is poorly formed, rather than choosing the built-in operator.

In this case, there is no conversion, so the derived operator+(double, double) actually not viable, but non-viable candidates are not eliminated until you build a set of candidates, and at the same time creating a set of candidates causes a tough error.

The proposed resolution to CWG 2052 will make this SFINAE case instead - it also does the source work. The problem is that Clang also uses the current version of the standard.

+4
source

He may complain because T may not be a class in this definition. And you are not allowed to override the operator+ standard for IIRC arithmetic types. In your example, there is no restriction of T on N::A

Adding typename = std::enable_if_t<std::is_class<T>{} || std::is_enum<T>{}> typename = std::enable_if_t<std::is_class<T>{} || std::is_enum<T>{}> seems to be a fix. Visual Studio and GCC may be a little more lazy / lazy regarding this limitation.

 namespace N { struct A {}; template <typename T, typename = std::enable_if_t<std::is_class<T>{} || std::is_enum<T>{}>> double operator+ (T a, double d) {return d;} template <typename T, typename = std::enable_if_t<std::is_class<T>{} || std::is_enum<T>{}>> double operator+ (double d, T a) {return d;} } void test() { N::A a; double x; double y = a + x; double z = x + a; } 
+2
source

All Articles