11

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.

manlio
  • 18,345
  • 14
  • 76
  • 126
DaviD.
  • 465
  • 3
  • 9

1 Answers1

5

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.

ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • Thanks to your great explanation and the link to the defect report I filed a bug report: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61663 – DaviD. Jul 01 '14 at 15:31