Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template conversion function to const-reference

The following works fine (as one would expect):

struct X {};

struct A
{
  operator X const& () 
  {
    static const X value{};
    return value; 
  }
};

int main()
{
  A a;
  X x = a;
}

But this is not so clear:

template<typename T>
struct X {};

struct A
{
  template<typename T>
  operator X<T> const& () 
  {
    static const X<T> value{};
    return value; 
  }
};

int main() 
{
  A a;
  X<int> x = a;
}

GCC 4.9 says error: conversion from ‘A’ to non-scalar type ‘X<int>’ requested whereas clang 3.4 has no problems with it. If you remove the const or the & from the conversion function or if you write X<int> const &x = a then GCC is happy, too.

So GCC only fails to find the conversion function if the target type is a const & to a template class and you request a conversion to a non-const & object of that class. Is this the correct behavior? I tried to read the standard but the overloading rules are quite confusing to me.

like image 564
DaviD. Avatar asked Jun 30 '14 14:06

DaviD.


1 Answers

Yes, this is a bug in gcc. This is almost precisely core DR976, the only difference being that in their example the destination type is a non-class type:

struct F {
   template<class T>
   operator const T&() { static T t; return t; }
};

int main() {
   F f;
   int i = f;   // ill-formed
}

As with your example, clang accepts and gcc rejects this example, regardless of the standard version dialect selected.

Note that the clause amended by that DR (14.8.2.3 [temp.deduct.conv]) does not discriminate between class and non-class destination types, so the resolution of that DR applies to your code equally.

Per the amended clause 14.8.2.3, the compiler should:

  • determine P, the return type of the conversion function template, as X<T> const &, and A, the required result type, as X<int>;
  • strip the reference from P, giving X<T> const;
  • strip the cv-qualification from P, giving X<T>;
  • deduce T as int.

I would recommend filing a bug report on https://gcc.gnu.org/bugzilla/ referencing that DR.


Because in your case you are converting to a class type, there is a workaround available:

X<int> x1 = a;          // fails
X<int> x2(a);           // OK
X<int> x3 = X<int>(a);  // also OK

In direct-initialization, constructors of the destination type are considered (8.5p16); the default copy constructor of X<int> takes a parameter of type X<int> const&, to which as we have already seen gcc is happy to convert a.

like image 140
ecatmur Avatar answered Nov 03 '22 10:11

ecatmur